API Kraken: проблемы с аутентификацией (недействительный ключ)

Я пытаюсь реализовать API биткойн-обмена Kraken на Java. К сожалению, я застрял в попытке выполнить аутентификацию, чтобы получить частные пользовательские данные.

В частности, я играл со следующей реализацией: http://pastebin.com/nHJDAbH8 Документация Kraken API находится здесь: https://www.kraken.com/help/api

Однако до сих пор я получал только { "error": [ "EAPI: Invalid key" ]}. Я не мог найти ошибки в реализации, и я попробовал несколько различных API-ключей. Может ли кто-нибудь, возможно, быстро взглянуть на реализацию и искать недостатки в коде? Или кто-то успешно реализовал API Kraken?

Большое спасибо!

Инструкции для аутентификации:

HTTP-заголовок: API-ключ = API-ключ API-Sign = подпись сообщения с использованием HMAC-SHA512 (путь URI + SHA256 (данные nonce + POST)) и base64 расшифрованный секретный ключ API

Почтовые данные: nonce = всегда увеличивая неподписанное 64-битное целое otp = двухфакторный пароль (если включен двухфакторный, в противном случае не требуется) Примечание: в моем случае otp отключен, поэтому пост-данные состоят только из nonce.

Реализация, с которой я экспериментировал, это:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class KrakenClient {
 protected static String key = "myAPIKey"; // API key
 protected static String secret = "MySecret===="; // API secret
 protected static String url = "api.kraken.com"; // API base URL
 protected static String version = "0"; // API version
 public static void main(String[] args) throws Exception {
 queryPrivateMethod("Balance");
 }
 public static void queryPrivateMethod(String method) throws NoSuchAlgorithmException, IOException{
 long nonce = System.currentTimeMillis();
 String path = "/" + version + "/private/" + method; // The path like "/0/private/Balance"
 String urlComp = "https://"+url+path; // The complete url like "https://api.kraken.com/0/private/Balance"
 String postdata = "nonce="+nonce;
 String sign = createSignature(nonce, path, postdata);
 postConnection(urlComp, sign, postdata);
 }
 /**
 * @param nonce
 * @param path
 * @param postdata
 * @return
 * @throws NoSuchAlgorithmException
 * @throws IOException
 */
 private static String createSignature(long nonce, String path,
 String postdata) throws NoSuchAlgorithmException, IOException {
 return hmac(path+sha256(nonce + postdata), new String(Base64.decodeBase64(secret)));
 }
 public static String sha256Hex(String text) throws NoSuchAlgorithmException, IOException{
 return org.apache.commons.codec.digest.DigestUtils.sha256Hex(text);
 }
 public static byte[] sha256(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException{
 MessageDigest md = MessageDigest.getInstance("SHA-256");
 md.update(text.getBytes());
 byte[] digest = md.digest();
 return digest;
 }
 public static void postConnection(String url1, String sign, String postData) throws IOException{
 URL url = new URL( url1 );
 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 connection.addRequestProperty("API-Key", key);
 connection.addRequestProperty("API-Sign", Base64.encodeBase64String(sign.getBytes()));
 // connection.addRequestProperty("API-Sign", sign);
 connection.addRequestProperty("User-Agent", "Mozilla/4.0");
 connection.setRequestMethod( "POST" );
 connection.setDoInput( true );
 connection.setDoOutput( true );
 connection.setUseCaches( false );
 // connection.setRequestProperty( "Content-Type",
 // "application/x-www-form-urlencoded" );
 connection.setRequestProperty( "Content-Length", String.valueOf(postData.length()) );
 OutputStreamWriter writer = new OutputStreamWriter( connection.getOutputStream() );
 writer.write( postData );
 writer.flush();
 BufferedReader reader = new BufferedReader(
 new InputStreamReader(connection.getInputStream()) );
 for ( String line; (line = reader.readLine()) != null; )
 {
 System.out.println( line );
 }
 writer.close();
 reader.close();
 }
 public static String hmac(String text, String secret){
 Mac mac =null;
 SecretKeySpec key = null;
 // Create a new secret key
 try {
 key = new SecretKeySpec( secret.getBytes( "UTF-8"), "HmacSHA512" );
 } catch( UnsupportedEncodingException uee) {
 System.err.println( "Unsupported encoding exception: " + uee.toString());
 return null;
 }
 // Create a new mac
 try {
 mac = Mac.getInstance( "HmacSHA512" );
 } catch( NoSuchAlgorithmException nsae) {
 System.err.println( "No such algorithm exception: " + nsae.toString());
 return null;
 }
 // Init mac with key.
 try {
 mac.init( key);
 } catch( InvalidKeyException ike) {
 System.err.println( "Invalid key exception: " + ike.toString());
 return null;
 }
 // Encode the text with the secret
 try {
 return new String( mac.doFinal(text.getBytes( "UTF-8")));
 } catch( UnsupportedEncodingException uee) {
 System.err.println( "Unsupported encoding exception: " + uee.toString());
 return null;
 }
 }
}
2 ответа

Вот как я работал с Haskell:

signature body nonce path secret = convertToBase Base64 hmacsha512
 where
 sha256 = convert (hash $ nonce `append` body :: Digest SHA256)
 hmacsha512 = hmac secretd (path `append` sha256) :: HMAC SHA512
 secretd = fromRight $ convertFromBase Base64 secret :: ByteString

Итак, вам нужно:

  • получить SHA256 хэш nonce + body, т.е. SHA256("1487687774151000nonce=1487687774151000")
  • добавить необработанные байты дайджеста к path (результат будет непечатан, пример пути для метода баланса "/0/private/Balance"),
  • получить дайджест HMAC SHA512 с использованием base64-decoded secret,
  • закодировать до Base64.


Удалите префикс "/" вашей переменной пути.

String path = version + "/private/" + method; // The path like "0/private/Balance"

licensed under cc by-sa 3.0 with attribution.