You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Objective-C生成促销优惠签名报错OSStatus -50问题排查

iOS促销优惠签名生成失败(OSStatus -50)问题分析与解决

问题概述

在Objective-C代码中生成App Store促销优惠签名时,每次调用返回nil,报错:The operation couldn’t be completed. (OSStatus error -50 - EC private key creation from data failed),私钥文件为Xcode项目主目录下的.p8文件。

错误原因分析

  • 私钥路径无效:直接使用项目主目录路径加载.p8文件,运行时App处于沙盒环境,无法访问项目目录的文件,导致私钥加载失败或内容异常。
  • 私钥处理不彻底:移除头尾标记时残留无关字符(如空格、特殊换行),导致Base64解码后的数据无效。
  • SecKey创建参数不匹配:PKCS#8格式的.p8私钥未正确解析,属性配置不符合要求。
  • 签名算法参数错误:使用了错误的签名填充方式,ECDSA签名需匹配SHA-256算法的对应参数。

解决办法

1. 修正私钥文件加载逻辑

将.p8文件添加到Xcode项目(确保勾选对应Target),通过NSBundle获取运行时有效路径:

// 替换原私钥加载代码
NSString *privateKeyPath = [[NSBundle mainBundle] pathForResource:@"你的密钥文件名(不带.p8后缀)" ofType:@"p8"];
if (!privateKeyPath) {
    NSLog(@"私钥文件未找到,请确认已添加到项目并勾选Target");
    return nil;
}
NSError *loadError = nil;
NSString *privateKeyString = [NSString stringWithContentsOfFile:privateKeyPath encoding:NSUTF8StringEncoding error:&loadError];
if (!privateKeyString || loadError) {
    NSLog(@"加载私钥失败:%@", loadError.localizedDescription);
    return nil;
}

2. 优化私钥内容处理

用正则表达式精准提取Base64部分,避免残留无关字符:

// 替换原私钥清理代码
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=-----BEGIN PRIVATE KEY-----)([\\s\\S]*?)(?=-----END PRIVATE KEY-----)" options:0 error:nil];
NSTextCheckingResult *match = [regex firstMatchInString:privateKeyString options:0 range:NSMakeRange(0, privateKeyString.length)];
if (!match) {
    NSLog(@"私钥格式错误");
    return nil;
}
NSString *privateKeyBase64 = [privateKeyString substringWithRange:match.range];
// 移除所有空白字符
privateKeyBase64 = [privateKeyBase64 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
privateKeyBase64 = [privateKeyBase64 stringByReplacingOccurrencesOfString:@"\n" withString:@""];
privateKeyBase64 = [privateKeyBase64 stringByReplacingOccurrencesOfString:@"\r" withString:@""];

NSData *privateKeyData = [[NSData alloc] initWithBase64EncodedString:privateKeyBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters];
if (!privateKeyData) {
    NSLog(@"Base64解码私钥失败");
    return nil;
}

3. 正确解析PKCS#8格式私钥

使用SecItemImport解析PKCS#8格式的私钥,创建有效SecKey引用:

+ (SecKeyRef)createSecKeyRefFromPrivateKeyData:(NSData *)privateKeyData {
    CFArrayRef items = NULL;
    NSDictionary *importParams = @{
        (__bridge id)kSecImportExportPassphrase: @"", // 私钥未加密则留空
        (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate
    };
    
    OSStatus status = SecItemImport((__bridge CFDataRef)privateKeyData,
                                    NULL, // 自动检测格式
                                    NULL, // 自动检测类型
                                    NULL,
                                    0,
                                    (__bridge CFDictionaryRef)importParams,
                                    NULL,
                                    &items);
    
    if (status != errSecSuccess || !items || CFArrayGetCount(items) == 0) {
        NSLog(@"解析PKCS#8私钥失败,状态码:%d", (int)status);
        if (items) CFRelease(items);
        return NULL;
    }
    
    CFDictionaryRef itemDict = CFArrayGetValueAtIndex(items, 0);
    SecKeyRef keyRef = (__bridge SecKeyRef)CFDictionaryGetValue(itemDict, kSecImportItemKey);
    if (keyRef) {
        CFRetain(keyRef); // 保留引用避免被提前释放
    }
    
    CFRelease(items);
    return keyRef;
}

4. 修正签名算法参数

促销优惠签名使用ES256(ECDSA with SHA-256),替换原SecKeyRawSign为适配的SecKeyCreateSignature(iOS 10+支持):

+ (NSData *)signData:(NSData *)data withPrivateKey:(NSData *)privateKey {
    SecKeyRef keyRef = [self createSecKeyRefFromPrivateKeyData:privateKey];
    if (!keyRef) {
        NSLog(@"创建私钥引用失败");
        return nil;
    }
    
    CFErrorRef error = NULL;
    NSData *signature = (__bridge_transfer NSData *)SecKeyCreateSignature(keyRef,
                                                                          kSecKeyAlgorithmECDSASignatureMessageX962SHA256,
                                                                          (__bridge CFDataRef)data,
                                                                          &error);
    CFRelease(keyRef);
    
    if (error != NULL) {
        NSLog(@"签名失败:%@", CFBridgingRelease(CFErrorCopyDescription(error)));
        CFRelease(error);
        return nil;
    }
    
    return signature;
}

5. 验证Payload格式

确保Payload字段顺序完全正确:bundleId → keyId → productId → offerId → appAccountToken(可选,空字符串)→ nonce字符串 → timestamp(整数),分隔符为\u2063(不可见分隔符),顺序错误会导致签名无效。

内容的提问来源于stack exchange,提问作者Umesh Y

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 05:49:57