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

iOS与macOS无外部库验签失败问题(含-9809等错误)

跨平台RSA验签排坑指南

看起来你在iOS/macOS和C#的RSA验签对接上踩了不少实打实的坑,我帮你梳理几个最可能出问题的点,一个个来排查修复:

1. 公钥创建的核心问题:格式不对

你提到用「去除头信息的Pem文件」生成公钥,但SecKeyCreateWithData要求的是DER编码的二进制公钥,而不是直接去掉头尾的Base64字符串。很多人都栽在这里:

  • 正确步骤:把PEM去掉-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----后,先做Base64解码得到DER格式的NSData,再传给SecKeyCreateWithData。
  • 另外,你的attributes里设置了kSecAttrIsPermanent:@YES,如果只是临时用公钥验签,完全没必要存到钥匙串,改成@NO还能避免旧公钥残留的干扰;同时要确保kSecAttrKeySizeInBits和C#那边生成的公钥长度完全一致(比如确实是2048位)。

修复后的公钥创建代码示例:

// 假设pemString是去除头尾的Base64字符串
NSData *derData = [[NSData alloc] initWithBase64EncodedString:pemString options:NSDataBase64DecodingIgnoreUnknownCharacters];

NSDictionary* attributes = @{
    (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
    (id)kSecAttrKeySizeInBits: @2048,
    (id)kSecPublicKeyAttrs: @{
        (id)kSecAttrIsPermanent: @NO, // 不需要持久化就设为NO
        (id)kSecAttrApplicationTag: tag1
    },
    (id)kSecAttrCanEncrypt:@YES,
    (id)kSecAttrCanVerify:@YES,
    (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic
};
CFErrorRef error = NULL;
SecKeyRef keyRef = SecKeyCreateWithData((__bridge CFDataRef)derData, (__bridge CFDictionaryRef)attributes, &error);

2. iOS验签的两个关键错误

编码不匹配:UTF-16字节序问题

C#里的Unicode编码是UTF-16 Little-Endian,而iOS的NSUTF16StringEncoding会跟随系统字节序,这会导致两边生成的原始数据哈希完全不一样。你需要明确指定小端编码:

NSData *plainData = [dataToSign dataUsingEncoding:NSUTF16LittleEndianStringEncoding];

SecKeyRawVerify的哈希包装问题

C#的RSACryptoProvider.SignData默认会把SHA256哈希值用ASN.1结构包装后再做PKCS#1填充,但你现在直接把CC_SHA256的原始哈希传进去,验签肯定失败。更推荐用SecKeyVerifySignature,它会自动处理ASN.1包装,代码更简洁不易错:

NSData *plainData = [dataToSign dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSASignaturePKCS1v15SHA256;
OSStatus status = SecKeyVerifySignature(keyRef, algorithm, (__bridge CFDataRef)plainData, (__bridge CFDataRef)signature, NULL);

if (status == errSecSuccess) {
    NSLog(@"验签成功");
} else {
    NSLog(@"验签失败,错误码:%d", (int)status);
}

3. macOS验签的参数顺序错误

你的SecVerifyTransformCreate调用完全搞反了参数!第二个参数应该传签名数据,而不是你传的self.digest(哈希值),这直接导致验签逻辑错误。修复后的代码:

CFErrorRef errorCF = NULL;
// 第二个参数传签名数据,不是digest!
SecTransformRef verifier = SecVerifyTransformCreate(keyRef, (__bridge CFDataRef)signatureData, &errorCF);
if (!verifier) {
    CFShow(errorCF);
    CFRelease(errorCF);
    return NO;
}

// 设置原始待验签数据
SecTransformSetAttribute(verifier, kSecTransformInputAttributeName, (__bridge CFTypeRef)plainData, &errorCF);
if (errorCF) {
    CFShow(errorCF);
    CFRelease(errorCF);
    CFRelease(verifier);
    return NO;
}

// 设置哈希算法
SecTransformSetAttribute(verifier, kSecDigestTypeAttribute, kSecDigestSHA2, &errorCF);
SecTransformSetAttribute(verifier, kSecDigestLengthAttribute, (__bridge CFNumberRef)@256, &errorCF);
if (errorCF) {
    CFShow(errorCF);
    CFRelease(errorCF);
    CFRelease(verifier);
    return NO;
}

// 执行验签
CFBooleanRef result = SecTransformExecute(verifier, &errorCF);
BOOL success = (result != NULL && CFBooleanGetValue(result));
CFRelease(verifier);

if (errorCF) {
    CFShow(errorCF);
    CFRelease(errorCF);
}
return success;

最后几个额外检查点

  • 确认C#那边用的是SHA256WithRSA签名:比如rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),确保padding和哈希算法和移动端一致。
  • 签名数据传递要完整:C#生成的签名是Base64字符串的话,移动端解码时要用NSDataBase64DecodingIgnoreUnknownCharacters选项,避免换行符、大小写等问题干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:34:07