将PEM公钥读入iOS系统

18

我有一个Base64公钥,是使用Java代码生成的:

RSAPublicKeySpec rsaKS = new RSAPublicKeySpec(modulus, pubExponent);
RSAPublicKey rsaPubKey = (RSAPublicKey) kf.generatePublic(rsaKS);
byte[] encoded = rsaPubKey.getEncoded();
String base64 = Base64.encodeToString(encoded, Base64.DEFAULT);
Log.e(null, "base64: " + base64);

这将导致一个Base64字符串。

在OSX上,我可以使用以下代码获取SecKeyRef:

// Create the SecKeyRef using the key data
CFErrorRef error = NULL;
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(parameters, kSecAttrKeyClass, kSecAttrKeyClassPublic);
SecKeyRef keyRef = SecKeyCreateFromData(parameters, (__bridge CFDataRef)[pubKey base64DecodedData], &error);

然而,在iOS中并没有SecKeyCreateFromData方法。

我可以使用这段代码在iOS中使用Base64字符串,将其添加到密钥链中,然后再次检索它作为SecKeyRef,但我非常不想只为了使用一次就将证书添加到密钥链中。

经过一些研究,似乎我应该能够使用SecCertificateCreateWithData从我拥有的Base64字符串创建一个在iOS中使用的证书,但是当我使用此代码时,我总是得到一个空的证书:

NSString* pespublicKey = @"MIGfMA0GCSqGSIb3....DCUdz/y4B2sf+q5n+QIDAQAB";
NSData* certData = [pespublicKey dataUsingEncoding:NSUTF8StringEncoding];
SecCertificateRef cert;
if ([certData length]) {
    cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certData);
    if (cert != NULL) {
        CFStringRef certSummary = SecCertificateCopySubjectSummary(cert);
        NSString* summaryString = [[NSString alloc] initWithString:(__bridge NSString*)certSummary];
        NSLog(@"CERT SUMMARY: %@", summaryString);
        CFRelease(certSummary);
    } else {
        NSLog(@" *** ERROR *** trying to create the SSL certificate from data located at %@, but failed", pespublicKey);
    }
}

你看过这篇文章吗? - Gannic
是的,那就是我从上面获取代码的地方,但是SecCertificateRef始终为NULL。 - Darren
你是否想要获取一个 SecKeyRef 而不是一个 SecCertificateRefSecCertificateCreateWithData 要求数据是“X.509证书的DER(Distinguished Encoding Rules)表示”。 - Joshua
你可能还想看一下苹果公司的这个示例,其中包含-[SecKeyWrapper addPeerPublicKey:keyBits:]方法。 - Joshua
是的,我想要一个SecKeyRef,但Gannic链接的文章首先创建了一个SecCertificateRef,然后从中获取了一个SecKeyRef。我之前已经看过这篇文章。 - Darren
你没有对字符串进行base64解码。尝试使用NSData *certData = [[NSData alloc] initWithBase64EncodedString:pespublickey options:0]; - jrtc27
1个回答

2
您需要先对密钥数据进行base64解码,然后再传递给SecCertificateCreateWithData()函数。该函数期望的是原始的、已解码的数据,而您目前传递的是base64编码的数据。请尝试类似以下方式的解决方法:
NSData *certData = [[NSData alloc] initWithBase64EncodedString:pespublicKey options:0];
cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certData);

更新:

你发送给你的iOS代码的是base64 DER编码的密钥,而不是DER或PEM编码的证书。因此,你看到的结果是预期的——你提供了一个不包含证书的DER编码数据块,它会返回一个空的证书引用,表示不存在的证书数据。

你有两个选项:

  1. 使用你已经找到的代码将密钥添加到钥匙串中,然后取回它。这似乎是在iOS上导入密钥以供使用的“iOS方式”。

  2. 使用公钥及其关联的私钥签署证书并将其导入到你的应用程序中,创建与该证书的临时信任关系,然后从证书信息中提取出公钥(例如:iOS SecKeyRef from NSString

为了使第二个选项生效,你的Java代码不仅需要具有公钥,还需要关联的私钥来生成签名证书。

根据你打算对SecKeyRef做什么,你可能会遇到问题。SecKeyRef值可以直接转换为SecKeychainItemRef值,用于在钥匙串服务函数中使用。如果SecKeyRef值不来自钥匙串,你的代码将会出错。阅读更多信息请参考文档


我发帖时没有看到jrtc27的评论,但他的评论与我的答案相同。 - hrunting
1
它仍然返回一个空的SecCertificateref! - Darren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接