Marvel API returns Invalid Credentials - swift

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()
}

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

Where does SecKeyCreateSignature get the key name for Keychain signing authorization dialog?

I have noticed a difference between certain keys in the Keychain with respect to how they appear in the Keychain signing dialog, and I cannot figure out why some are displayed a certain way while others are not.
Here is some test code to use identities in the Keychain to sign a sample bit of data.
func testCreateSignature() throws {
let query: [String: Any] = [kSecClass as String: kSecClassIdentity,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: false,
kSecReturnRef as String: true,
kSecReturnData as String: true]
var resultsRef: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &resultsRef)
guard status == errSecSuccess else { throw SecurityError.unhandledError(status: status) }
guard let results = resultsRef as? [[String:Any]] else {
throw SecurityError.unexpectedCertificateData
}
let data = Data([0xDE, 0xAD, 0xBE, 0xEF])
var privateKey: SecKey!
for result in results {
let secIdentity = result[kSecValueRef as String] as! SecIdentity
try SecIdentityCopyPrivateKey(secIdentity, &privateKey).check()
var error: Unmanaged<CFError>?
let signature = SecKeyCreateSignature(privateKey, .rsaSignatureMessagePKCS1v15SHA1, data as CFData, &error)!
if let error = error {
throw error.takeRetainedValue()
}
print(signature)
}
}
When the code attempts to use one of the keys that Xcode installed for code signing, the resulting dialog looks like the following:
However, when the code attempts to use a key that I've installed, no matter what the label on the key in the Keychain is, it always looks like this:
When my app attempts to use a key to sign, I would like the user to see the name of the key the app wants to use, instead of just generic "privateKey", but I cannot find where this information might be stored on the key.
I have checked the kSecAttrLabel and kSecAttrApplicationLabel attributes of both identities and the private keys and cannot find the text that appears in the dialogs.
I found it. It is a property of the Access Control List of a Keychain item. See 'descriptor' param for SecAccessCreate.
If you do not specify a custom ACL when importing a key, it will default to "privateKey".
I was using SecPKCS12Import to import a .pfx file. I attempted to set the kSecImportExportAccess key in the options parameter to a custom SecAccess object, but it would always import with a default ACL.
I ended up refactoring the code to use SecItemImport instead to import the .pfx file and supplied a custom SecAccess instance:
static func importIdentity(contentsOf url: URL, password: String) throws {
let data = try Data.init(contentsOf: url)
var access: SecAccess!
try SecAccessCreate("License Key" as CFString, nil, &access).check()
var keychain: SecKeychain!
var outItems: CFArray?
let filename: CFString? = url.isFileURL ? url.lastPathComponent as CFString : nil
var inputFormat: SecExternalFormat = .formatPKCS12
var itemType: SecExternalItemType = .itemTypeAggregate
let unmanagedPassword = Unmanaged<AnyObject>.passRetained(password as AnyObject)
let unmanagedAccess = Unmanaged<SecAccess>.passRetained(access)
var params: SecItemImportExportKeyParameters = SecItemImportExportKeyParameters(version: UInt32(SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION),
flags: .importOnlyOne,
passphrase: unmanagedPassword,
alertTitle: nil,
alertPrompt: nil,
accessRef: unmanagedAccess,
keyUsage: nil,
keyAttributes: nil)
try SecKeychainCopyDefault(&keychain).check()
try SecItemImport(data as CFData, filename, &inputFormat, &itemType, [], &params, keychain, &outItems).check()
}
Importing the identity as above will result in "License Key" being shown in the signing dialog rather than "privateKey".

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)

Kraken EAPI: Invalid key

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.

How do I encode an unmanaged<SecKey> to base64 to send to another server?

I'm trying to use key pair encryption to validate identity between my app and my PHP server. To do this I need to send the public key over to the server after I generate it in my app.
if let pubKey = NSData(base64EncodedData: publicKey, options: NSDataBase64DecodingOptions.allZeros)! {
println(pubKey)
}
publicKey is of type Unmanaged<SecKey>.
The error I'm getting in the above code is: Extra argument 'base64EncodedData' in call
How would I do this? Is there a better way?
Edit: This is how the keypair is generated:
var publicKeyPtr, privateKeyPtr: Unmanaged<SecKey>?
let parameters = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): 2048
]
let result = SecKeyGeneratePair(parameters, &publicKeyPtr, &privateKeyPtr)
let publicKey = publicKeyPtr!.takeRetainedValue()
let privateKey = privateKeyPtr!.takeRetainedValue()
let blockSize = SecKeyGetBlockSize(publicKey)
Edit 2: So the issue is that SecKey is not NSData, so my question here should be: How do I convert a publicKey:SecKey to NSData?
It seems that you can temporary store the key to keychain and then get it back and convert it to data:
func convertSecKeyToBase64(inputKey: SecKey) ->String? {
// First Temp add to keychain
let tempTag = "de.a-bundle-id.temp"
let addParameters :[String:AnyObject] = [
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): tempTag,
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecValueRef): inputKey,
String(kSecReturnData):kCFBooleanTrue
]
var keyPtr: Unmanaged<AnyObject>?
let result = SecItemAdd(addParameters, &keyPtr)
switch result {
case noErr:
let data = keyPtr!.takeRetainedValue() as! NSData
// Remove from Keychain again:
SecItemDelete(addParameters)
let encodingParameter = NSDataBase64EncodingOptions(rawValue: 0)
return data.base64EncodedStringWithOptions(encodingParameter)
case errSecDuplicateItem:
println("Duplicate Item")
SecItemDelete(addParameters)
return nil
case errSecItemNotFound:
println("Not found!")
return nil
default:
println("Error: \(result)")
return nil
}
}
While the fact is barely documented, you can pull out everything you need (that is, modulus and exponent) from the SecKey using SecKeyCopyAttributes.
See here for the details.
Swift 4 method to get base64 string from SecKey, publicKey :)
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey!, nil) else {
NSLog("\tError obtaining export of public key.")
return ""
}
let publicKeyNSData = NSData(data: publicKeyData as Data)
let publicKeyBase64Str = publicKeyNSData.base64EncodedString()