I am trying to implement the API of the bitcoin exchange Kraken in MATLAB. Unfortunately I got stuck at trying to execute an authentication in order to retrieve private user data.
In particular, I was playing with the following Implementation: Kraken API MATLAB client invalid signature error. The documentation of Kraken's API is here: https://www.kraken.com/help/api
When connecting with the Private user data but I continuously run into the error: {"error":["EAPI:Invalid signature"]}. Could someone maybe have a quick look at the implementation below and look for flaws in the code? Or has someone successfully implemented the Kraken API for Matlab?
Many thanks!
% Private
uri = '0/private/Balance';
postdata='';
[response,status] = kraken_authenticated(uri,postdata)
% test uri='0/private/AddOrder'
% test postdata='&pair=XBTEUR&type=buy&ordertype=limit&price=345.214&volume=0.65412&leverage=1.5&oflags=post'
function [response,status]=kraken_authenticated(uri,postdata)
% Generate URL
url=['https://api.kraken.com/',uri];
% nonce
nonce = num2str(floor((now-datenum('1970', 'yyyy'))*8640000000));
key = ' '
secret = ' '
% 1st hash
Opt.Method = 'SHA-256';
Opt.Input = 'ascii';
sha256string = DataHash(['nonce=',nonce,postdata],Opt);
% 2nd hash
%sign = crypto([uri,sha256string], secret, 'HmacSHA512');
sign = crypto([uri,sha256string], base64decode(secret), 'HmacSHA512')
%sign = HMAC([uri,sha256string], base64decode(secret), 'SHA-512');
%header_0=http_createHeader('Content-Type','application/x-www-form-urlencoded');
header_1=http_createHeader('API-Key',key);
header_2=http_createHeader('API-Sign',char(sign));
header=[header_1 header_2];
[response,status] = urlread2(url,'POST',['nonce=',nonce,postdata],header);
end
function signStr = crypto(str, key, algorithm)
import java.net.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.*
keyStr = java.lang.String(key);
key = SecretKeySpec(keyStr.getBytes('UTF-8'), algorithm);
%key = SecretKeySpec(keyStr.getBytes(), algorithm)
mac = Mac.getInstance(algorithm);
mac.init(key);
toSignStr = java.lang.String(str);
signStr = java.lang.String(Hex.encodeHex( mac.doFinal( toSignStr.getBytes('UTF-8'))))
%signStr = java.lang.String(Hex.encodeHex( mac.doFinal( toSignStr.getBytes())))
end
function header = http_createHeader(name,value)
header = struct('name',name,'value',value);
end
I'm actually trying to do my owm implementation in c++, and I was here for another error I get, but here is a possible cause I noticed in your code :
The first sha256 hash should be of a concatenation of nonce and postdata. As postdata is also containing the nonce, if the nonce value is 123456789, then you should do (pseudo-code) :
sha256("123456789nonce=123456789")
or in Matlab :
sha256string = DataHash([nonce,'nonce=',nonce],Opt);
I hope it helps.
Related
If I import a seed phrase with #solana/web3.js I seem to get different public address than the ones generated by Phantom wallet when I import the same seed phrase. Anyone any idea why?
const seed = Bip39.mnemonicToSeedSync("<12 word phrase>").slice(0, 32);
const mint_authority = web3.Keypair.fromSeed(seed)
do I need to do anything with derivation paths so that addresses generated match those of Phantom wallet ?
You can use this code.
It runs correctly in my side.
seed_bytes = Bip39SeedGenerator(cfg.ETH_TEMP_MNEMONIC).Generate()
bip44_mst_ctx = Bip44.FromSeed(seed_bytes, Bip44Coins.SOLANA)
for i in range(100):
bip44_acc_ctx = bip44_mst_ctx.Purpose().Coin().Account(i)
bip44_chg_ctx = bip44_acc_ctx.Change(Bip44Changes.CHAIN_EXT)
new_wallet = WalletData()
new_wallet.public_key = bip44_chg_ctx.PublicKey().ToAddress()
new_wallet.private_key = Base58Encoder.Encode(
bip44_chg_ctx.PrivateKey().Raw().ToBytes() + bip44_chg_ctx.PublicKey().RawCompressed().ToBytes()[1:]
)
I have an offline signing code to which I need to pass the digest or the binary blob to get the signature. Looks like the digest I have produced is not right as when I try to submit the serialized signature, it complains of "fails local checks: Invalid signature." Here are the steps, I am doing to generate the digest/binary blob
STTx noopTx(ttPAYMENT,
[&](auto& obj)
{
// General transaction fields
obj[sfAccount] = id;
obj[sfFee] = STAmount(XRPAmount(fee));
obj[sfFlags] = tfFullyCanonicalSig;
obj[sfSequence] = sequence;
obj[sfSigningPubKey] = pub_key.slice();
// Payment-specific fields
obj[sfAmount] = STAmount(XRPAmount(amount));
obj[sfDestination] = *to_account;
obj[sfSendMax] = STAmount(XRPAmount(amount));
});
ripple::uint256 hash256 = noopTx.getSigningHash();
output:
0861970E8AAC8539600E2FB9169774F303A29C3B8CA98FF9206C9B958C681ACF
Please can someone tell me if I am missing any field that is needed?.
We are developing a REST web service with the WS security headers to be passed through as header parameters in the REST request.
I am testing this in SoapUI Pro and want to create a groovy script to generate these and then use them in the REST request.
These parameters include the password digest, encoded nonce and created dateTime and password digest which is created from encoding the nonce, hashed password and created date and time, i.e. the code should be the same as that which generates these from using the Outgoing WS Security configurations in SoapUI Pro.
I have created a groovy test script in Soap UI Pro (below). However when I supply the created values to the headers I get authorisation errors.
I am able to hash the password correctly and get the same result a my python script.
Groovy code for this is ..
MessageDigest cript = MessageDigest.getInstance("SHA-1");
cript.reset();
cript.update(userPass.getBytes("UTF-8"));
hashedpw = new String(cript.digest());
This correctly hashes the text 'Password2451!' to í¦è~µ”t5Sl•Vž³t;$.
The next step is to create a password digest of the nonce the created time stamp and the hashed pasword. I have the following code for this ...
MessageDigest cript2 = MessageDigest.getInstance("SHA-1");
cript2.reset();
cript2.update((nonce+created+hashedpw).getBytes("UTF-8"));
PasswordDigest = new String(cript2.digest());
PasswordDigest = PasswordDigest.getBytes("UTF-8").encodeBase64()
This converts '69999998992017-03-06T16:19:28Zí¦è~µ”t5Sl•Vž³t;$' into w6YA4oCUw6nDicucw6RqxZMIbcKze+KAmsOvBA4oYu+/vQ==.
However the correct value should be 01hCcFQRjDKMT6daqncqhN2Vd2Y=.
The following python code correctly achieves this conversion ...
hashedpassword = sha.new(password).digest()
digest = sha.new(nonce + CREATIONDATE + hashedpassword).digest()
Can anyone tell me where I am going wrong with the groovy code?
Thanks.
changing my answer slightly as in original I was converting the pasword digest to a string value which caused the request to not validate some of the time as certain bytes did not get converted into the correct string value.
import java.security.MessageDigest;
int a = 9
nonce = ""
for(i = 0; i < 10; i++)
{
random = new Random()
randomInteger= random.nextInt(a)
nonce = nonce + randomInteger
}
Byte[] nonceBytes = nonce.getBytes()
def XRMGDateTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss", TimeZone.getTimeZone( 'BTC' ));
Byte[] creationBytes = XRMGDateTime.getBytes()
def password = testRunner.testCase.testSuite.getPropertyValue( "XRMGPassword" )
EncodedNonce = nonce.getBytes("UTF-8").encodeBase64()
MessageDigest cript = MessageDigest.getInstance("SHA-1");
cript.reset();
cript.update(password.getBytes());
hashedpw = cript.digest();
MessageDigest cript2 = MessageDigest.getInstance("SHA-1");
cript2.update(nonce.getBytes());;
cript2.update(XRMGDateTime.getBytes());
cript2.update(hashedpw);
PasswordDigest = cript2.digest()
EncodedPasswordDigest = PasswordDigest.encodeBase64();
def StringPasswordDigest = EncodedPasswordDigest.toString()
def encodedNonceString = EncodedNonce.toString()
testRunner.testCase.setPropertyValue( "passwordDigest", StringPasswordDigest )
testRunner.testCase.setPropertyValue( "XRMGDateTime", XRMGDateTime )
testRunner.testCase.setPropertyValue( "XRMGNonce", encodedNonceString )
testRunner.testCase.setPropertyValue( "Nonce", nonce )
I'm trying to do some authenticated calls to Kraken private endpoints but without success. I'm still getting an error EAPI:Invalid signature.
Does anybody know what's wrong?
Here's the code:
function [response,status]=kraken_authenticated(uri,postdata)
% test uri='0/private/AddOrder'
% test postdata='&pair=XBTEUR&type=buy&ordertype=limit&price=345.214&volume=0.65412&leverage=1.5&oflags=post'
url=['https://api.kraken.com/',uri];
% nonce
nonce = num2str(floor((now-datenum('1970', 'yyyy'))*8640000000));
[key,secret]=key_secret('kraken');
% 1st hash
Opt.Method = 'SHA-256';
Opt.Input = 'ascii';
sha256string = DataHash(['nonce=',nonce,postdata],Opt);
% 2nd hash
sign = crypto([uri,sha256string], secret, 'HmacSHA512');
header_1=http_createHeader('API-Key',key);
header_2=http_createHeader('API-Sign',char(sign));
header=[header_1 header_2];
[response,status] = urlread2(url,'POST',['nonce=',nonce,postdata],header);
end
Crypto function is in another file:
function signStr = crypto(str, key, algorithm)
import java.net.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.*
keyStr = java.lang.String(key);
key = SecretKeySpec(keyStr.getBytes('UTF-8'), algorithm);
mac = Mac.getInstance(algorithm);
mac.init(key);
toSignStr = java.lang.String(str);
signStr = java.lang.String(Hex.encodeHex( mac.doFinal( toSignStr.getBytes('UTF-8'))));
end
I've also tried
sign = crypto([uri,sha256string], base64decode(secret), 'HmacSHA512');
but without success.
This is guide for authenticated call HTTPS Header:
API-Key = API key
API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key
This is guide for authenticated call POST Data:
nonce = always increasing unsigned 64 bit integer
otp = two-factor password (if two-factor enabled, otherwise not required)
I've tried to pass "nonce" parameter or all parameters in "postdata" to POST data but without success.
Thanks for help.
The problem is in function crypto here:
keyStr = java.lang.String(key);
key = SecretKeySpec(keyStr.getBytes('UTF-8'), algorithm);
As the base64 encoded private key from kraken is not necessarily UTF-8 encoded, you cannot use UTF-8 encoding to extract the key and pass UTF-8 string to the SecretKeySpec function. You need to use byte array instead.
Similar issues
https://code.google.com/p/google-apps-script-issues/issues/detail?id=5113
https://code.google.com/p/google-apps-script-issues/issues/detail?id=3121
Solution for javascript
github.com/Caligatio/jsSHA
I made a test suite for math:hmac_* KRL functions. I compare the KRL results with Python results. KRL gives me different results.
code: https://gist.github.com/980788 results: http://ktest.heroku.com/a421x68
How can I get valid signatures from KRL? I'm assuming that they Python results are correct.
UPDATE: It works fine unless you want newline characters in the message. How do I sign a string that includes newline characters?
I suspect that your python SHA library returns a different encoding than is expected by the b64encode library. My library does both the SHA and base64 in one call so I to do some extra work to check the results.
As you show in your KRL, the correct syntax is:
math:hmac_sha1_base64(raw_string,key);
math:hmac_sha256_base64(raw_string,key);
These use the same libraries that I use for the Amazon module which is testing fine right now.
To test those routines specifically, I used the test vectors from the RFC (sha1, sha256). We don't support Hexadecimal natively, so I wasn't able to use all of the test vectors, but I was able to use a simple one:
HMAC SHA1
test_case = 2
key = "Jefe"
key_len = 4
data = "what do ya want for nothing?"
data_len = 28
digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79
HMAC SHA256
Key = 4a656665 ("Jefe")
Data = 7768617420646f2079612077616e7420666f72206e6f7468696e673f ("what do ya want for nothing?")
HMAC-SHA-256 = 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
Here is my code:
global {
raw_string = "what do ya want for nothing?";
mkey = "Jefe";
}
rule first_rule {
select when pageview ".*" setting ()
pre {
hmac_sha1 = math:hmac_sha1_hex(raw_string,mkey);
hmac_sha1_64 = math:hmac_sha1_base64(raw_string,mkey);
bhs256c = math:hmac_sha256_hex(raw_string,mkey);
bhs256c64 = math:hmac_sha256_base64(raw_string,mkey);
}
{
notify("HMAC sha1", "#{hmac_sha1}") with sticky = true;
notify("hmac sha1 base 64", "#{hmac_sha1_64}") with sticky = true;
notify("hmac sha256", "#{bhs256c}") with sticky = true;
notify("hmac sha256 base 64", "#{bhs256c64}") with sticky = true;
}
}
var hmac_sha1 = 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79';
var hmac_sha1_64 = '7/zfauXrL6LSdBbV8YTfnCWafHk';
var bhs256c = '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843';
var bhs256c64 = 'W9zBRr9gdU5qBCQmCJV1x1oAPwidJzmDnexYuWTsOEM';
The HEX results for SHA1 and SHA256 match the test vectors of the simple case.
I tested the base64 results by decoding the HEX results and putting them through the base64 encoder here
My results were:
7/zfauXrL6LSdBbV8YTfnCWafHk=
W9zBRr9gdU5qBCQmCJV1x1oAPwidJzmDnexYuWTsOEM=
Which match my calculations for HMAC SHA1 base64 and HMAC SHA256 base64 respectively.
If you are still having problems, could you provide me the base64 and SHA results from python separately so I can identify the disconnect?