首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从DER/PEM文件中获取SecKeyRef

如何从DER/PEM文件中获取SecKeyRef
EN

Stack Overflow用户
提问于 2012-05-14 16:41:23
回答 3查看 25.8K关注 0票数 22

我需要集成我的iPhone应用程序与系统,他们需要加密数据的一个给定的公钥,有3个文件在3种不同的格式.xml .der和.pem,我已经研究和找到了一些文章从DER/PEM获取SecKeyRef,但他们总是返回零。下面是我的代码:

代码语言:javascript
复制
NSString *pkFilePath = [[NSBundle mainBundle] pathForResource:@"PKFile" ofType:@"der"];
NSData *pkData = [NSData dataWithContentsOfFile:pkFilePath]; 

SecCertificateRef   cert; 
cert = SecCertificateCreateWithData(NULL, (CFDataRef) pkData);
assert(cert != NULL);

OSStatus err;

    if (cert != NULL) {
        err = SecItemAdd(
                         (CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
                                            (id) kSecClassCertificate,  kSecClass, 
                                            (id) cert,                  kSecValueRef,
                                            nil
                                            ], 
                         NULL
                         );
        if ( (err == errSecSuccess) || (err == errSecDuplicateItem) ) {
            CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL); 
            SecPolicyRef policy = SecPolicyCreateBasicX509();
            SecTrustRef trust;
            SecTrustCreateWithCertificates(certs, policy, &trust);
            SecTrustResultType trustResult;
            SecTrustEvaluate(trust, &trustResult);
            if (certs) {
                CFRelease(certs);
            }
            if (trust) {
                CFRelease(trust);
            }
            return SecTrustCopyPublicKey(trust);
        }
    }
return NULL;

问题发生在SecCertificateCreateWithData上,它总是返回nil,即使读取文件是ok的。有没有人这样做过,请帮帮我,谢谢!

编辑:证书文件是MD5签名。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-25 18:48:11

我在同样的问题上挣扎了很多,最终找到了解决方案。我的问题是,我需要同时使用外部私钥和公钥来加密/解密iOS应用程序中的数据,而不想使用密钥链。事实证明,您还需要一个签名证书才能使iOS安全库能够读取密钥数据,当然,文件必须采用正确的格式。该过程基本如下:

假设您有一个PEM格式的私钥(带有-BEGIN RSA private key - and -END RSA PRIVATE KEY- markers):rsaPrivate.pem

代码语言:javascript
复制
//Create a certificate signing request with the private key
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr

//Create a self-signed certificate with the private key and signing request
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt

//Convert the certificate to DER format: the certificate contains the public key
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der

//Export the private key and certificate to p12 file
openssl pkcs12 -export -out rsaPrivate.p12 -inkey rsaPrivate.pem -in rsaCert.crt

现在您有两个与iOS安全框架兼容的文件: rsaCert.der (公钥)和rsaPrivate.p12 (私钥)。下面的代码读取公钥,假设文件已添加到捆绑包中:

代码语言:javascript
复制
- (SecKeyRef)getPublicKeyRef {

    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaCert" ofType:@"der"];
    NSData *certData = [NSData dataWithContentsOfFile:resourcePath];
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)certData);
    SecKeyRef key = NULL;
    SecTrustRef trust = NULL;
    SecPolicyRef policy = NULL;

    if (cert != NULL) {
        policy = SecPolicyCreateBasicX509();
        if (policy) {
            if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) {
                SecTrustResultType result;
                OSStatus res = SecTrustEvaluate(trust, &result);

                //Check the result of the trust evaluation rather than the result of the API invocation.
                if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
                    key = SecTrustCopyPublicKey(trust);
                }
            }
        }
    }
    if (policy) CFRelease(policy);
    if (trust) CFRelease(trust);
    if (cert) CFRelease(cert);
    return key;
}

要读入私钥,请使用以下代码:

代码语言:javascript
复制
SecKeyRef getPrivateKeyRef() {
    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaPrivate" ofType:@"p12"];
    NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath];

    NSMutableDictionary * options = [[NSMutableDictionary alloc] init];

    SecKeyRef privateKeyRef = NULL;

    //change to the actual password you used here
    [options setObject:@"password_for_the_key" forKey:(id)kSecImportExportPassphrase];

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    OSStatus securityError = SecPKCS12Import((CFDataRef) p12Data,
                                             (CFDictionaryRef)options, &items);

    if (securityError == noErr && CFArrayGetCount(items) > 0) {
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        SecIdentityRef identityApp =
        (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                             kSecImportItemIdentity);

        securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
        if (securityError != noErr) {
            privateKeyRef = NULL;
        }
    }
    [options release];
    CFRelease(items);
    return privateKeyRef;
}
票数 57
EN

Stack Overflow用户

发布于 2017-08-29 02:05:06

从PEM10开始,实际上可以导入iOS私钥,但不需要将其转换为PKCS#12 (这是一种非常通用的容器格式,适用于与加密相关的所有内容),因此也可以在命令行上使用OpenSSL或静态链接应用程序。在macOS上,甚至可以在10.7中使用与这里提到的函数不同的函数(但到目前为止,iOS还不存在)。不过,下面描述的方法也适用于macOS 10.12和更高版本。

要导入证书,只需剥离

代码语言:javascript
复制
-----BEGIN CERTIFICATE-----

代码语言:javascript
复制
-----END CERTIFICATE-----

行,然后对剩下的数据运行base64解码,结果是一个标准DER格式的证书,它可以直接提供给SecCertificateCreateWithData()以获得SecCertificateRef。这一直有效,在iOS 10之前也是如此。

要导入私钥,可能需要做一些额外的工作。如果私钥是用

代码语言:javascript
复制
-----BEGIN RSA PRIVATE KEY-----

那就很简单了。同样,第一行和最后一行需要剥离,其余数据需要进行base64解码,结果是PKCS#1格式的RSA。这种格式只能保存RSA密钥,并且是直接可读的,只需将解码后的数据送入SecKeyCreateWithData()即可获得SecKeyRefattributes字典只需要以下键/值对:

在密钥中包含位数的

  • kSecAttrKeyTypekSecAttrKeyTypeRSA
  • kSecAttrKeyClass
  • kSecAttrKeySizeInBitskSecAttrKeyClassPrivate CFNumberRef (例如,1024、2048等)如果不知道,这些信息实际上可以从原始关键字数据中读取,这些数据是ASN.1数据(这有点超出了本答案的范围,但我将在下面提供一些关于如何解析该格式的有用链接)。这个值可能是可选的!在我的测试中,实际上没有必要设置这个值;如果没有这个值,API会自己确定这个值,并且以后总是正确地设置它。

如果私钥被-----BEGIN PRIVATE KEY-----封装,那么base64编码的数据不是PKCS#1格式,而是PKCS#8格式,然而,这只是一个更通用的容器,也可以保存非RSA,但是对于RSA,该容器的内部数据等于PKCS#1,所以可以说对于RSA,PKCS#8是带有额外报头的PKCS#1,您所需要做的就是剥离额外的报头。只需剥离base64解码数据的前26个字节,就可以再次得到PKCS#1。是的,就是这么简单。

要了解更多关于PEM编码中的PKCS#x格式的信息,请访问have a look at this site。要了解有关ASN.1格式的更多信息,请访问here's a good site for that。如果你需要一个简单的,但强大的,交互式的在线ASN.1解析器来处理不同的格式,一个可以直接读取PEM数据的解析器,以及base64和hexdump,try this site中的ASN.1。

非常重要:在添加私钥到密钥链时,请注意这样的私钥不包含公钥散列,但公钥散列对于密钥链应用编程接口形成身份(SecIdentityRef)很重要,因为使用公钥散列是应用编程接口如何找到属于导入证书的正确私钥( SecIdentityRef只是私钥的SecKeyRef和形成组合对象的证书的SecCertificateRef,正是公钥散列将它们绑定在一起)。因此,当您计划将私钥添加到keychain时,请确保手动设置公钥散列,否则您将无法获得它的标识,否则您将无法使用keychain API执行签名或解密数据等任务。公钥散列必须存储在一个名为kSecAttrApplicationLabel的属性中(我知道这是一个愚蠢的名字,但它实际上不是一个标签,用户什么也看不到,请查看文档)。例如:

代码语言:javascript
复制
OSStatus error = SecItemAdd(
    (__bridge CFDictionaryRef)@{
        (__bridge NSString *)kSecClass: 
            (__bridge NSString *)kSecClassKey,
        (__bridge NSString *)kSecAttrApplicationLabel: 
             hashOfPublicKey, // hashOfPublicKey is NSData *
#if TARGET_OS_IPHONE
        (__bridge NSString *)kSecValueRef: 
            (__bridge id)privateKeyToAdd, // privateKeyToAdd is SecKeyRef
#else
        (__bridge NSString *)kSecUseItemList: 
              @[(__bridge id)privateKeyToAdd], // privateKeyToAdd is SecKeyRef
              // @[ ... ] wraps it into a NSArray object,
              // as kSecUseItemList expects an array of items
#endif
     },
     &outReference // Can also be NULL,
                   // otherwise reference to added keychain entry
                   // that must be released with CFRelease()
);
票数 16
EN

Stack Overflow用户

发布于 2017-09-22 05:29:33

在这篇文章的帮助下,经过几个小时的在线搜索,我终于让它完美地工作了。以下是最新版本的Swift代码的注释。我希望它能帮助一些人!

  1. 收到了夹在头部和尾部之间的base64编码字符串格式的证书,如下所示(PEM格式):

-BEGIN CERTIFICATE-END CERTIFICATE-----

  • strip out头部和尾部,例如//删除头部字符串let CERTIFICATE-----

  • strip=(“-BEGIN CERTIFICATE-”).characters.count let index = certStr.index(cerStr.startIndex,offsetBy: offset+1) cerStr = cerStr.substring(from: index) //去掉尾部字符串let tailWord =“-END CERTIFICATE-”if let lowerBound = cerStr.range(of: tailWord)?.lowerBound { cerStr = cerStr.substring( to : lowerBound) }

  • 将base64字符串解码为NSData:

让data = NSData(base64Encoded: cerStr,options:NSData.Base64DecodingOptions.ignoreUnknownCharacters)!

  • 将其从NSdata格式转换为SecCertificate:

设证书= SecCertificateCreateWithData(kCFAllocatorDefault,数据)

  • 现在,此证书可用于与从urlSession信任收到的证书进行比较:

certificateFromUrl = SecTrustGetCertificateAtIndex(...)如果证书证书{ }

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10579985

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档