我如何在iOS中使用PKCS8 RSA DER私钥?

在运行时,我的iOS应用程序接收一个由公有私有RSA密钥对的文件,由其他人的Java生成:

KeyPairGenerator keygenerator; keygenerator = KeyPairGenerator.getInstance("RSA"); keygenerator.initialize(4096); KeyPair keypair = keygenerator.generateKeyPair(); PrivateKey privateKey = keypair.getPrivate().getEncoded(); PublicKey publicKey = keypair.getPublic().getEncoded(); 

我已经成功地读取和使用公钥,使用这种方法 ,从密钥剥去一些序言。

我现在想要使用私钥 。 同样的方法不起作用,我认为序言是不同的。 博客build议它导入PKCS#1 PEM密钥,但接着说它们是二进制的,所以我认为它们只是Base64编码的DER密钥。 我还发现,也许我所拥有的密钥是PKCS#8编码。

当然可以使用

 openssl pkcs8 -nocrypt -inform der < pk8.der > pvt.pem 

在示例私钥和openssl不抱怨。

公钥是PKCS#1还是私有PKCS#8?

但是我真的想要使用CommonCrypto和安全框架,而不是在OpenSSL上进行链接。 在Mac OS上有libsecurity中的函数来读取PKCS#8,但是这还没有到iOS。 老实说,我尝试阅读源代码,但是我无法弄清楚他们究竟是在什么地方剥下钥匙。

[TL; DR]如何从DER私钥中剥离版本和algorithm PKCS#8字段,并使用CommonCrypto或一些C / C ++ / ObjC获取普通密钥?

没有OpenSSL,我无法解决这个问题。 所以这是一个使用OpenSSL的解决scheme。

假设你有一个名为privateKey的NSData和key,另一个名字是signableData,你想签名。

 #import <openssl/x509.h> #import <openssl/pem.h> NSURL *cacheDir = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject]; NSString *infile = [[cacheDir URLByAppendingPathComponent:@"privkey.der"] path]; NSError *error; [privateKey writeToFile:infile options:NSDataWritingFileProtectionComplete error:&error]; if (error) { NSLog(@"%@", error); } else { BIO *in = BIO_new_file([infile cStringUsingEncoding:NSUTF8StringEncoding], "rb"); PKCS8_PRIV_KEY_INFO *p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(in, NULL); NSLog(@"%i", p8inf->broken); EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf); PKCS8_PRIV_KEY_INFO_free(p8inf); BIO_free(in); uint8_t * cipherBuffer = NULL; // Calculate the buffer sizes. unsigned int cipherBufferSize = RSA_size(pkey->pkey.rsa); unsigned int signatureLength; // Allocate some buffer space. I don't trust calloc. cipherBuffer = malloc(cipherBufferSize); memset((void *)cipherBuffer, 0x0, cipherBufferSize); unsigned char *openSSLHash = SHA1(signableData.bytes, signableData.length, NULL); int success = RSA_sign(NID_sha1, openSSLHash, 20, cipherBuffer, &signatureLength, pkey->pkey.rsa); if (success) NSLog(@"WIN"); NSData *signed = [NSData dataWithBytes:(const void*)cipherBuffer length:signatureLength]; EVP_PKEY_free(pkey); } 

你可以在这个网页上看到一个der键在ASN1结构中的外观: https : //lapo.it/asn1js/

以下是SwCrypt库中的一个代码,它从私钥中剥离了PKCS8头。 这是Swift,但您可以轻松地将其重写为任何其他语言。

  static private func stripHeaderIfAny(keyData: NSData) throws -> NSData { var bytes = keyData.arrayOfBytes() var offset = 0 guard bytes[offset] == 0x30 else { throw SwError.ASN1Parse } offset += 1 if bytes[offset] > 0x80 { offset += Int(bytes[offset]) - 0x80 } offset += 1 guard bytes[offset] == 0x02 else { throw SwError.ASN1Parse } offset += 3 //without PKCS8 header if bytes[offset] == 0x02 { return keyData } let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] let slice: [UInt8] = Array(bytes[offset..<(offset + OID.count)]) guard slice == OID else { throw SwError.ASN1Parse } offset += OID.count guard bytes[offset] == 0x04 else { throw SwError.ASN1Parse } offset += 1 if bytes[offset] > 0x80 { offset += Int(bytes[offset]) - 0x80 } offset += 1 guard bytes[offset] == 0x30 else { throw SwError.ASN1Parse } return keyData.subdataWithRange(NSRange(location: offset, length: keyData.length - offset)) }