MacOS 10.9下SecKeyCreateFromData调用失败(错误码-2147415792)
解决MacOS 10.9下Security框架构造RSA公钥失败的问题
你的代码在10.11+版本的MacOS上正常运行,但在10.9上调用失败,核心原因是10.9的Security框架对ASN.1模板的处理逻辑更严格,和后续版本存在兼容性差异。下面是具体的排查方向和解决方案:
1. 补全ASN.1模板的终止项
10.9的SecAsn1Template要求模板数组必须以{0}作为结尾,否则解码函数会因为读取越界而失败(10.11+的框架做了容错处理,忽略了这个问题)。
修正后的模板应该是这样:
static const SecAsn1Template kRsaPublicKeyTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ASN1_RSA_PUBLIC_KEY) }, { SEC_ASN1_INTEGER, offsetof(ASN1_RSA_PUBLIC_KEY, modulus) }, { SEC_ASN1_INTEGER, offsetof(ASN1_RSA_PUBLIC_KEY, publicExponent) }, { 0 } // 必须添加:标记模板结束 };
2. 确保输入数据符合DER整数格式
10.9的Security框架对DER编码的整数格式要求更严格:
- 如果整数的最高位是1,必须添加一个前导零字节(避免被解析为负数)
- 不能包含多余的前导零(比如模数的前导零字节会直接导致解码失败)
举个例子,公钥指数65537(0x10001)的正确DER编码是:02 03 01 00 01(02是INTEGER标签,03是长度,后面是实际的3字节数据)。如果你的代码直接传入原始的指数字节(没有标签和长度),或者带了多余的前导零,10.9的解码就会失败。
3. 使用更兼容的SecKeyCreateFromData构造公钥
相比手动处理ASN.1模板,SecKeyCreateFromData是封装性更好、兼容性更强的API,10.9及以后的系统都支持。下面是适配后的构造函数代码:
#include <Security/Security.h> #include <CoreFoundation/CoreFoundation.h> class CRSAPublicKey { private: SecKeyRef m_publicKey = nullptr; public: CRSAPublicKey(const unsigned char* pExponent, const std::size_t nExponentSize, const unsigned char* pModulus, const std::size_t nModulusSize) { // 自定义兼容的RSA公钥结构体,避免系统版本差异 typedef struct { SecAsn1Item modulus; SecAsn1Item publicExponent; } MyASN1_RSA_PUBLIC_KEY; MyASN1_RSA_PUBLIC_KEY rsaPubKey = { .modulus = { .data = const_cast<unsigned char*>(pModulus), .length = nModulusSize }, .publicExponent = { .data = const_cast<unsigned char*>(pExponent), .length = nExponentSize } }; static const SecAsn1Template kRsaPublicKeyTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(MyASN1_RSA_PUBLIC_KEY) }, { SEC_ASN1_INTEGER, offsetof(MyASN1_RSA_PUBLIC_KEY, modulus) }, { SEC_ASN1_INTEGER, offsetof(MyASN1_RSA_PUBLIC_KEY, publicExponent) }, { 0 } // 必须的终止项 }; CFErrorRef error = nullptr; // 先将结构体编码为DER格式的数据 CFDataRef derData = SecAsn1Encode(kRsaPublicKeyTemplate, &rsaPubKey, &error); if (!derData) { if (error) { // 打印错误信息,方便排查 CFStringRef errorDesc = CFErrorCopyDescription(error); CFShow(errorDesc); CFRelease(errorDesc); CFRelease(error); } return; } // 使用SecKeyCreateFromData构造公钥 m_publicKey = SecKeyCreateFromData(nullptr, derData, kSecAttrKeyTypeRSA, kSecAttrKeyClassPublic, &error); if (!m_publicKey && error) { CFStringRef errorDesc = CFErrorCopyDescription(error); CFShow(errorDesc); CFRelease(errorDesc); CFRelease(error); } CFRelease(derData); } // 析构函数释放SecKeyRef ~CRSAPublicKey() { if (m_publicKey) { CFRelease(m_publicKey); } } };
4. 高效错误排查技巧
在10.9上调试时,一定要捕获CFErrorRef的错误信息——它会明确告诉你是ASN.1解码错误、数据格式不对还是API参数问题,比盲目排查高效得多。比如用CFErrorCopyDescription(error)获取可读的错误描述,再针对性修复。
内容的提问来源于stack exchange,提问作者Rudolfs Bundulis
相关产品推荐
相关产品推荐

