My requirements are to generate a signature for a promotional offer in the iOS app code (Objective-C), but I'm getting nil every time with the error: 'The operation couldn’t be completed. (OSStatus error -50 - EC private key creation from data failed).' Please refer to my code below and suggest how I can get the signature. The privateKeyPath is a .p8 file saved in the Xcode project's main folder:
+ (NSString *)generateSignatureWithKeyId:(NSString *)keyId
bundleId:(NSString *)bundleId
productId:(NSString *)productId
offerId:(NSString *)offerId
nonce:(NSUUID *)nonce
timestamp:(NSTimeInterval)timestamp
privateKeyPath:(NSString *)privateKeyPath {
// Load the private key
NSString *privateKeyString = [NSString stringWithContentsOfFile:privateKeyPath encoding:NSUTF8StringEncoding error:nil];
if (!privateKeyString) {
NSLog(@"Failed to load private key");
return nil;
}
// Remove the header and footer
privateKeyString = [privateKeyString stringByReplacingOccurrencesOfString:@"-----BEGIN PRIVATE KEY-----" withString:@""];
privateKeyString = [privateKeyString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
privateKeyString = [privateKeyString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
privateKeyString = [privateKeyString stringByReplacingOccurrencesOfString:@"-----END PRIVATE KEY-----" withString:@""];
// Convert to NSData
NSData *privateKeyData = [[NSData alloc] initWithBase64EncodedString:privateKeyString options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSLog(@"NSDATA private key: %@", [privateKeyData base64EncodedStringWithOptions:0]);
if (!privateKeyData) {
NSLog(@"Failed to decode private key");
return nil;
}
// Create the payload string
NSString *payloadString = [NSString stringWithFormat:@"%@\u2063%@\u2063%@\u2063%@\u2063%@\u2063%@\u2063%.0f",
bundleId,
keyId,
productId,
offerId,
@"", // appAccountToken is optional, leave empty if not used
[nonce UUIDString],
timestamp];
NSData *payloadData = [payloadString dataUsingEncoding:NSUTF8StringEncoding];
// Sign the payload
NSData *signature = [self signData:payloadData withPrivateKey:privateKeyData];
if (!signature) {
NSLog(@"Failed to sign data");
return nil;
}
// Base64 encode the signature
NSString *signatureBase64 = [self base64UrlEncode:signature];
return signatureBase64;
}
+ (NSData *)signData:(NSData *)data withPrivateKey:(NSData *)privateKey {
SecKeyRef keyRef = [self createSecKeyRefFromPrivateKeyData:privateKey];
if (!keyRef) {
NSLog(@"Failed to create private key reference.");
return nil;
}
size_t sigLen = SecKeyGetBlockSize(keyRef);
uint8_t *sig = malloc(sigLen);
if (sig == NULL) {
NSLog(@"Failed to allocate memory for signature.");
return nil;
}
OSStatus status = SecKeyRawSign(keyRef, kSecPaddingPKCS1, data.bytes, data.length, sig, &sigLen);
if (status != errSecSuccess) {
NSLog(@"Failed to sign data: %d", (int)status);
free(sig);
return nil;
}
return [NSData dataWithBytes:sig length:sigLen];
}
+ (SecKeyRef)createSecKeyRefFromPrivateKeyData:(NSData *)privateKeyData {
NSString *base64EncodedString = [privateKeyData base64EncodedStringWithOptions:0];
SecKeyRef privateKeyRef = NULL;
NSDictionary *privateKeyAttr = @{
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
(id)kSecAttrKeySizeInBits: @256,
(id)kSecAttrIsPermanent: @NO
};
CFErrorRef error = NULL;
privateKeyRef = SecKeyCreateWithData((__bridge CFDataRef)privateKeyData,
(__bridge CFDictionaryRef)privateKeyAttr,
&error);
if (error != NULL) {
NSString *errorDescription = CFBridgingRelease(CFErrorCopyDescription(error));
NSLog(@"Error creating private key reference: %@", errorDescription);
CFRelease(error);
privateKeyRef = NULL;
}
return privateKeyRef;
}
So, I’m presuming that the call that actually fails is SecKeyCreateWithData
. If so, the most likely reason is that you’re key byte aren’t in the expected format. I talked about this in depth in On Cryptographic Key Formats and the related Importing Cryptographic Keys.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"