Reputation: 16482
I'm trying to port this Java code to Swift to create a signature from private key.
The private key is generated using these instructions.
https://walmart.io/key-tutorial
openssl genrsa -des3 -out WM_IO_my_rsa_key_pair 2048
https://walmart.io/docs/affiliate/onboarding-guide
public String generateSignature(String key, String stringToSign) throws Exception {
Signature signatureInstance = Signature.getInstance("SHA256WithRSA");
ServiceKeyRep keyRep = new ServiceKeyRep(KeyRep.Type.PRIVATE, "RSA", "PKCS#8", Base64.decodeBase64(key));
PrivateKey resolvedPrivateKey = (PrivateKey) keyRep.readResolve();
signatureInstance.initSign(resolvedPrivateKey);
byte[] bytesToSign = stringToSign.getBytes("UTF-8");
signatureInstance.update(bytesToSign);
byte[] signatureBytes = signatureInstance.sign();
String signatureString = Base64.encodeBase64String(signatureBytes);
return signatureString;
}
This is my attempt, but it's not working. It fails at SecKeyCreateWithData
.
func createHash(string: String) -> Data {
let hash = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(CC_SHA256_DIGEST_LENGTH))
let hashLength = string.count
defer { hash.deallocate() }
CC_SHA256(string, CC_LONG(hashLength), hash)
return Data(bytes: hash, count: hashLength)
}
func createSignature(string: String) throws -> String? {
guard let url = Bundle.module.url(forResource: "WM_IO_private_key", withExtension: "pem") else {
return nil
}
let privateKey = try String(contentsOf: url, encoding: .utf8)
.replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "")
.split(separator: "\n").joined()
var error: Unmanaged<CFError>?
guard let privateKeyData = Data(base64Encoded: privateKey, options: .ignoreUnknownCharacters) else {
return nil
}
let attributes: [NSObject : NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits: NSNumber(value: 2048),
kSecReturnPersistentRef: true as NSObject
]
guard let secKey = SecKeyCreateWithData(privateKeyData as CFData, attributes as CFDictionary, &error) else {
return nil
}
let hash = createHash(string: string)
let algorithm: SecKeyAlgorithm = .rsaSignatureDigestPSSSHA256
guard let signature = SecKeyCreateSignature(secKey, algorithm, hash as CFData, &error) as Data? else {
throw error!.takeRetainedValue() as Error
}
return signature.base64EncodedString()
}
Upvotes: 0
Views: 1218
Reputation: 3264
The commando you used to create the key encrypts the key. As far as I know, this is not supported by SecKeyCreateWithData
. You can remove the encryption by running the following command and entering your password.
openssl rsa -in WM_IO_private_key -out WM_IO_private_key
Remember, the private key is now unencrypted.
func signature(string: String) throws -> String? {
guard let url = Bundle.module.url(forResource: "WM_IO_private_key", withExtension: "") else {
return nil
}
let derString = try String(contentsOf: url)
.replacingOccurrences(of: "-----BEGIN RSA PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "-----END RSA PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "\n", with: "")
guard let derData = Data(base64Encoded: derString, options: .ignoreUnknownCharacters) else {
return nil
}
let attributes: [CFString: Any] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits: 2048
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateWithData(derData as CFData, attributes as CFDictionary, &error) else {
throw error!.takeUnretainedValue()
}
// Instead of creating the hash yourself, let the security framework do it for you
// by using the rsaSignatureMessagePKCS1v15SHA256 algorithm. Alternatively you could
// use rsaSignatureMessagePSSSHA256, as PSS is a better algorithm.
// Make sure the key supports signing with the algorithm
guard SecKeyIsAlgorithmSupported(privateKey, .sign, .rsaSignatureMessagePKCS1v15SHA256) else {
return nil
}
guard let signature = SecKeyCreateSignature(privateKey, .rsaSignatureMessagePKCS1v15SHA256, Data(string.utf8) as CFData, &error) else {
throw error!.takeUnretainedValue()
}
return (signature as Data).base64EncodedString()
}
Upvotes: 1
Reputation: 1
As of today, auth signature code is available in Java (https://www.walmart.io/docs/affiliate/onboarding-guide) The idea we provided sample code to help the customers to implement the logic at customer end by referring it. You can implement the logic in(C# .NET, Python, PHP or JS) in such a way that whenever your system invoking Walmart APIs, generate the signature on the fly and pass as input parameter. This is how all of customers implemented and consuming our APIs.
Please refer the below documentation for complete. https://walmart.io/docs/affiliate/quick-start-guide https://www.walmart.io/docs/affiliate/onboarding-guide
Regards, Firdos IO Support
Upvotes: 0