Generating a signature for promotional offers in Objective-c

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;
}
Answered by DTS Engineer in 795113022

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"

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"

Thanks for the quick and detailed response. I'm still going through your explanation, but due to time constraints, I would like to know a more straightforward solution. After downloading the .p8 file from Apple Developer, how can I proceed in Objective-C code to generate a signature?

I am requesting Apple to make subscription offers presentation easier. For example, by creating a method with the main subscription product ID and the offer ID, the offer can be presented to the user without signature generation.

Have a great day.

Generating a signature for promotional offers in Objective-c
 
 
Q