2012-09-07 145 views
3

在运行时,我的iOS应用程序接收与公共和私营部门RSA密钥对的文件,通过别人的Java产生:如何在iOS中使用PKCS8 RSA DER私钥?

KeyPairGenerator keygenerator; 
keygenerator = KeyPairGenerator.getInstance("RSA"); 
keygenerator.initialize(4096); 

KeyPair keypair = keygenerator.generateKeyPair(); 
PrivateKey privateKey = keypair.getPrivate().getEncoded(); 
PublicKey publicKey = keypair.getPublic().getEncoded(); 

我已经成功地阅读和使用的公共键,使用this method,它从关键字剥去一些序言。

我现在想用私钥键。同样的方法不起作用,我认为序言在某种程度上是不同的。博客建议它导入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私钥剥离version and algorithm PKCS#8字段,并使用CommonCrypto或某些C/C++/ObjC获取普通密钥?

回答

0

没有OpenSSL,我无法解决问题。所以这里有一个解决方案,确实使用OpenSSL

假设你有一个名为privateKey的NSData和关键字,另一个叫你想签名的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); 
} 
+0

这不适合我。我用 替换了前两行NSString * path = [[NSBundle mainBundle] pathForResource:@“keyNew”ofType:@“pem”]; NSString * infile = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; 和我总是得到错误,它不会在其他部分。如果我删除了,如果还有,那么它给我错误“BIO * in”。 –

+0

@NiteshMeshram你是否试图将密钥写入捆绑包?你不能在那里写文件。 'infile'应该是密钥文件的* path *(作为NSString),而不是文件的内容。在你的版本中,用'path'替换'BIO * in'开始的行上的'infile',并且删除它上面的所有代码(导入除外),保持'NSString * path = [[NSBundle mainBundle] pathForResource: @“keyNew”ofType:@“pem”];'。 – Cathy

+0

不,我有.pem文件,我只想读取它。任何建议,我已经尝试了两个和读/写,但它不工作。基本上我想只读... –

0

你可以看到一个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)) 
    }