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