Kraken EAPI: Invalid key - swift

I am trying to do a client with swift however I cannot communicate with the private api {"error": ["EAPI: Invalid key"]}
I use CCHmac function and tried to have a look at different api implementation.
I just can't get whats wrong...
Here is my code:
func getTradeBalance(basedAsset: String, completionBlock: #escaping BlockResult) {
guard let url = URL(string: "\(krakenUtils.rootUrl)/\(krakenUtils.version)/private/Balance") else {
return
}
let nonce: String = String(Int(Date().timeIntervalSince1970.rounded()))
let path = "/\(krakenUtils.version)/private/Balance"
let pubKey = krakenUtils.publicKey
let params = ["nonce": nonce]
//Sign = HMAC-SHA512 of URI + SHA256-POSTDATAS + base64decodedSecret
let sign = getMessageSignature(path: path,
nonce: nonce)
Alamofire.request(url, method: .post,
parameters: params, encoding: JSONEncoding.default,
headers: ["API-Key": pubKey,
"API-Sign": sign])
.responseJSON { resp in
let result = self.handleResponse(result: resp)
guard let json = result.0 else {
completionBlock(nil, result.1)
return
}
print(json)
}
}
private func getMessageSignature(path: String, nonce: String) -> String {
let secretDecoded = Data(base64Encoded: krakenUtils.privateKey, options: Data.Base64DecodingOptions.init(rawValue: 0))!
let np = (nonce + "nonce=" + nonce).sha256().data(using: .utf8, allowLossyConversion: false)!
var pathNP = path.data(using: .utf8, allowLossyConversion: false)!
pathNP.append(contentsOf: np)
let lRet = HMAC.sign(data: pathNP, algorithm: .sha512, key: secretDecoded).base64EncodedString()
return lRet
}
public static func sign(data: Data, algorithm: Algorithm, key: Data) -> Data {
let signature = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: algorithm.digestLength)
data.withUnsafeBytes { dataBytes in
key.withUnsafeBytes { keyBytes in
CCHmac(algorithm.algorithm, keyBytes, key.count, dataBytes, data.count, signature)
}
}
return Data(bytes: signature, count: algorithm.digestLength)
}
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)

For the API-Key you use krakenUtils.publicKey. The name suggests you use some public key (do not know where you got it)
However this should be your personal API-Key. You can get it at the kraken site (login with your account) and create an API key. You also get your API-code here. They go together as a pair

Although this is an old question, many users (including myself) who have attempted to use Kraken’s API have encountered and reported similar problems (INVALID KEY). Recently I reported the issue I was having to a Kraken rep…after an exchange with several reps it was discovered that there was a flaw in the posted Kraken example on their website. Here is some of the exchange:
...”In the method QueryPrivateEndpoint there is a line of code (should be line 256 from the file downloaded) that looks like this:
String apiEndpointFullURL = baseDomain + privatePath + endPointName + "?" + inputParameters;
It needs to be modified to look like this:
String apiEndpointFullURL = baseDomain + privatePath + endPointName;
After you make that code change, the Invalid Key error should go away.
…….
Thank you for your gratitude and pointing out the mistake to our attention.
We are pleased to know the issue has been resolved.
“……
They will post the update to their example code on their website.
Also, it’s worth noting that an improper/inconsistent ORDER of the parameters in a call that has parameters associated with it can cause an INVALID KEY error.

Related

P384 Public Key getting "IncorrectParameterSize"

I am working on the ECDSA Algorithm where i am taking signature from the APIs and i have one local public key inside constant file.
Below is my code when i am trying to run this and verify the signature then i am getting error in this link
let publicKey = try P384.Signing.PublicKey.init(derRepresentation: privateKeyPemBytes)
I am receiving "incorrectParameterSize" this error from the catch block. Can anyone having idea about this Encryption algorithm and help me out?
func verifySignature(signedData: Data, signature: Data, publicKey: String) -> Bool {
var result = false
do {
if #available(iOS 14.0, *) {
let decodedData = Data(base64Encoded: publicKey)
let publicKeyPemBytes = [UInt8](decodedData!)
let publicKey = try P384.Signing.PublicKey.init(derRepresentation: privateKeyPemBytes)
let hash = SHA256.hash(data: signedData)
let signing384 = try P384.Signing.ECDSASignature(derRepresentation: signature)
if publicKey.isValidSignature(signing384, for: hash) {
result = true
}
} else {
// Fallback on earlier versions
}
} catch {
print("\(error)")
}
return result
}
In the discussion it turned out that the public key has the X.509/SPKI format, is ASN.1/DER encoded and is available as Base64 encoded string.
However, it is a P-256 key and not a P-384 key, which is the cause of the error. The fix is to use P256 in the code instead of P384.
The signature is in ASN.1/DER format, as Base64 encoded string.
Swift supports both the ASN.1/DER signature format and the P1363 signature format for ECDSA signatures (s. here for the difference).
The following code shows the verification for both signature formats using sample data:
import Foundation
import Crypto
// message to be signed
let msg = "The quick brown fox jumps over the lazy dog".data(using: .utf8)!
let hash = SHA256.hash(data: msg)
// public key import
let publicKeyX509DerB64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg=="
let publicKeyX509Der = Data(base64Encoded: publicKeyX509DerB64)
let publicKey = try! P256.Signing.PublicKey.init(derRepresentation: publicKeyX509Der!)
// verifying an ASN.1/DER encoded signature
let signatureDerB64 = "MEYCIQCDMThF51R3S3CfvtVQomSO+kotOMH6HfvVcx04a21QwQIhAP2Patj0N3CVoeB6yiZt/gEVh9qQ7mtyvF4FiWBYtE0a"
let signatureDer = Data(base64Encoded: signatureDerB64)
let signature1 = try! P256.Signing.ECDSASignature(derRepresentation: signatureDer!)
print(publicKey.isValidSignature(signature1, for: hash)) // true
// verifying a P1363 encoded signature
let signatureP1363B64 = "gzE4RedUd0twn77VUKJkjvpKLTjB+h371XMdOGttUMH9j2rY9DdwlaHgesombf4BFYfakO5rcrxeBYlgWLRNGg=="
let signatureP1363 = Data(base64Encoded: signatureP1363B64)
let signature2 = try! P256.Signing.ECDSASignature(rawRepresentation: signatureP1363!)
print(publicKey.isValidSignature(signature2, for: hash)) // true

Marvel API returns Invalid Credentials

I'm using Marvel API.
I set up timestamp, apikey and hash(ts+privateKey+apiKey).md5
// ts
let ts = String(Date().timeIntervalSince1970)
// apiKey
"cfd8392fa89be1a574b402a9c54f9c52"
// privateKey
"d3ea745cf73f7a7278e1368ffe3b50f4bdb2746"
I tested using Postman.But A Response is always 401 Invalid Credentials.
/// URL
https://gateway.marvel.com:443/v1/public/characters?ts=1649942740.2140222&apikey=cfd8392fa89be1a574b402a9c54f9c52&hash=d9a73e6105271137942b5ea743ae2ad2
{
"code": "InvalidCredentials",
"message": "That hash, timestamp and key combination is invalid."
}
And I already set up an authorized referee with *(asterisk) at My Developer Account - Marvel
Please help me. I don't know what I missed.
Convert the timestamp, private and public key to md5 hash. Then use that for the query string.
import CryptoKit
let ts = String(Date().timeIntervalSince1970)
let hash = MD5(string:"\(ts)\("PRIVATE_KEY")\("PUBLIC_KEY")")
func MD5(string: String) -> String {
let digest = Insecure.MD5.hash(data: string.data(using: .utf8) ?? Data())
return digest.map {
String(format: "%02hhx", $0)
}.joined()
}

Swift: HMAC SHA-512 for coinspot API

I am trying to connect to the coinspot API which has very shoddy documentation. Link below:
https://www.coinspot.com.au/api
It requires an HMAC SHA512 hash attached to the user's secret key, then a nonce key In the parameters of a POST request which is a timestamp turned to an integer to stay unique at each request. The header requires both the API key and the signature.
I use Alamofire's JSON encoded post request through coinspot's private API URL https://www.coinspot.com.au/api
Seems I get a success message for connecting but get an {status = invalid;} response.
The only 2 things I can think of are a wrong POST request (which doesn't seem the case, as if I don't add the nonce key in the parameters, I get a proper response requesting for one from the server), and the HMAC SHA-512 hash. I've taken the code from
HMAC SHA512 using CommonCrypto in Swift 3.1
And modified it slightly to just include the SHA512 hashing.
Code below:
extension String{
var sha512:String{
return HMAC.hash(inp: self, algo: HMACAlgo.SHA512)
}
}
public struct HMAC{
static func hash(inp: String, algo: HMACAlgo)->String{
if let stringData = inp.data(using: String.Encoding.utf8, allowLossyConversion: false){
return hexStringFromData(input: digest(input: stringData as NSData, algo: algo))
}
return ""
}
private static func digest(input: NSData, algo: HMACAlgo)-> NSData{
let digestLength = algo.digestLength()
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA512(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private static func hexStringFromData(input: NSData)-> String{
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes{
hexString += String(format: "%02x", UInt8(byte))
}
return hexString
}
}
enum HMACAlgo{
case SHA512
func digestLength()->Int{
var result:CInt = 0
switch self{
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
The CommonCrypto library has been properly added with an objectiveC bridging file, and printing the result gives me a proper hashed signature.
I've contacted coinspot but they are swarmed with requests and they only have a cookie template response in regards to any technical support for their API.
Here's the Alamofire segment I placed under a function to call any command to the coinspot server:
func request(_ command: Router, params: [String: AnyObject]?, completion: #escaping (Result<JSON>)-> Void){
var parameters = ["nonce": Int(Date().timeIntervalSince1970*1000) as AnyObject]
if params != nil{
parameters = parameters + params!
}
let sign = customerSecret.sha512
print(sign)
let headers:HTTPHeaders = ["key": APIKey, "sign": sign]
do{
try Alamofire.request( command.asURL(), method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON{(response) in
guard let json = response.result.value else {
completion(.failure(CoinSpotError.networkError(error: response.result.error)))
return
}
print(response)
}
} catch {
print("error")
}
}
I think the above is correct. Router is just a set of addresses that call to the proper POST request for retrieving data.
Finally, I've changed the request to GET, got rid of both headers and parameters, and I get a status ok and the resulting JSON. I figured I'll just try a command on a browser and it worked,
The API does say every request must be a post method.
Coinspot API is down, with no ETA: Coinbase API down
I continue to receive {'status': 'invalid'} back from any request I make.
Their publicapi is still up, albeit only for BTC, LTC, and DOGE.
def get_latest_price():
r = requests.get("https://www.coinspot.com.au/pubapi/latest").json()
print(r)

Cloudant function-clause error at HTTP GET request

This is my first question here and I have not much experience in coding so please bear with me. Thanks!
I defined some documents in my Bluemix Cloudant account with different cars which have different characteristics. I want to get one entry from an IOS Swift front-end App.
This is an example query url:
https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q=size:small
Now the problem: If I use this url in a browser I get the correct results in JSON format back without any error. But if the app makes the request a function-clause error is logged while the request itself seems to be successful.
I read that a function_clause error is caused by some bug in the Javascript Cloudant uses for indexing the documents. The Javascript I'm using is exactely the same as Cloudant states it in the tutorials.
Has anyone an idea why it works in the browser but not in the App?
Thank you very much for any help!
Here is all the code:
This is the method I use in swift to make the request:
func databaseRequest(size: String, interior: String, fuel: String) {
let baseURL = "https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q="
let queryURL = "size:\(size)"
let completeURL: String = baseURL + queryURL
let completeURLModified = completeURL.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
let requestURL = URL(string: completeURLModified!)
var request = URLRequest(url: requestURL!)
request.httpMethod = "GET"
request.setValue("Basic \(credentials)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request){data, response, error in
guard error == nil else{
print("There was an error:", error as Any)
return
}
guard data == data else{
print("Data is empty")
return
}
let jsonResponse = try! JSONSerialization.jsonObject(with: data!, options: [])
print("This is JSON Response", jsonResponse)
}; task.resume()
}
This is the response from the JSON answer:
This is JSON Response {
error = "unknown_error";
reason = "function_clause";
ref = 1944801346;
}
The rest of log from http headers if this is helpful:
Optional(<NSHTTPURLResponse: 0x6080000349c0> { URL: https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q=size:small } { status code: 500, headers {
"Cache-Control" = "must-revalidate";
"Content-Length" = 70;
"Content-Type" = "application/json";
Date = "Thu, 24 Nov 2016 04:41:03 GMT";
Server = "CouchDB/2.0.0 (Erlang OTP/17)";
"Strict-Transport-Security" = "max-age=31536000";
Via = "1.1 lb1.bm-cc-dal-01 (Glum/1.31.3)";
"X-Cloudant-Backend" = "bm-cc-dal-01";
"X-Content-Type-Options" = nosniff;
"X-Couch-Request-ID" = 51e5e0b5e1;
"X-Couch-Stack-Hash" = 1944801346;
"X-CouchDB-Body-Time" = 0;
Last but not least the Javascript file I use as Index in the design document in Cloudant:
function (doc) {
index("name", doc.name, {"store": true});
if (doc.fuel){ index("fuel", doc.fuel, {"store": true});}
if (doc.interior){ index("interior", doc.interior, {"store": true});}
if (doc.size){index("size", doc.size, {"store": true});
}}
I think this error is due to cloudant trying to decode whatever you passed as \(credentials) as a base64 encoded string. If \(credentials) is not a valid base64 encoded string (e.g. contains characters other than a-z, A-Z, 0-9, +, / and =), then my guess is that cloudant's base64 decoding function fails with the above error.
You need to make sure that \(credentials) is the string <your_username>:<your_password> encoded correctly. E.g. if your username is john and your password is doe, then \(credentials) should be am9objpkb2U=.

Kraken private API with F# returns EGeneral: invalid arguments

I am trying to access the Kraken private API using F#. The code to access the public API runs perfectly fine, but when i try to access the private API i am always getting the error "EGeneral:Invalid arguments".
#r "FSharp.Data.dll"
open FSharp.Data
open System
open System.Text
open System.Security.Cryptography
let baseUri = "https://api.kraken.com"
let key = MY_KRAKEN_API_KEY
let secret = MY_KRAKEN_API_SECRET
let path = "/0/private/Balance"
let nonce = DateTime.UtcNow.Ticks
let bodyText = "nonce=" + nonce.ToString()
let hmac (key : byte []) (data : byte[]) =
use hmac = new HMACSHA512(key)
hmac.ComputeHash(data)
let sha256 (data : string) =
use sha = SHA256Managed.Create()
sha.ComputeHash(Encoding.UTF8.GetBytes(data))
let createSignature (nonce : int64) body (path : string) secret =
let shaSum = nonce.ToString() + body |> sha256
let data = Array.append (Encoding.UTF8.GetBytes path) shaSum
let key = Convert.FromBase64String secret
hmac key data |> Convert.ToBase64String
let signature = createSignature nonce bodyText path secret
let response = Http.RequestString (
url = baseUri + path,
httpMethod = "POST",
headers = ([("API-Key", key); ("API-Sign", signature)] |> Seq.ofList),
body = TextRequest bodyText
)
Does anybody see what i am doing wrong?
EDIT:
The Kraken.com API documentation is awailable here: https://www.kraken.com/help/api
I suppose the header signature is incorrect. The docu requires the following two values to be submitted in the 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
EDIT 2:
The remaining parameters need to be transmitted with a POST method. In my case this is only the "nonce" value in the body part of the HTTP request.
I had the same error while I was writing a C# library for Kraken and I found a solution of this problem:
This error does not appears if the API key or the sign are wrong or missing. The problem is that you do not add a mediatype to your request. I do not know how it works in F# but look at this example:
using (var client = new HttpClient())
{
string address = String.Format("{0}/{1}/public/{2}", _url, _version, method);
// Does not work with this:
// var content = new StringContent(postData, Encoding.UTF8);
var content = new StringContent(postData, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await client.PostAsync(address, content);
return await response.Content.ReadAsStringAsync();
}
The "application/x-www-form-urlencoded" is the critical path. If you do not send a request with that, you get the "EGeneral: invalid arguments"-error. With it, everything works fine. At least in my case.