¿Cómo puedo usar una key privada PKCS8 RSA DER en iOS?

En time de ejecución, mi aplicación iOS recibe un file con un par de keys RSA público-privado, generado por Java de otra persona:

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

He leído y utilizado correctamente la key pública , usando este método , que elimina un cierto preámbulo de la key.

Ahora quiero usar la key privada . El mismo método no funciona, asumí que el preámbulo es diferente de alguna manera. El blog sugirió que importaba keys PEM de PKCS n. ° 1, pero luego dice que son binarias, por lo que creo que solo significan keys DER codificadas en Base64. También encontré que tal vez las keys que tengo están codificadas en PKCS # 8 .

Ciertamente puedo usar

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

en una key privada de muestra y openssl no se queja.

¿Tendría sentido que la key pública fuera PKCS # 1 y privada PKCS # 8?

Pero realmente me gustaría usar CommonCrypto y el Marco de security en lugar de vincular contra OpenSSL si es posible . En Mac OS hay funciones en libsecurity para leer PKCS # 8, pero esto todavía no lo ha hecho en iOS. Lo hice, honestamente, trato de leer la fuente, pero no puedo determinar dónde tiran la llave.

[TL; DR] ¿Cómo puedo quitar la versión y el algorithm de los campos PKCS # 8 de la key privada DER y simplemente get la key simple, utilizando CommonCrypto o alguna C / C ++ / ObjC?

No pude resolver el problema sin OpenSSL. Entonces, aquí hay una solución que usa OpenSSL.

Suponiendo que tiene un NSData llamado privateKey con la key, y otro llamado signableData que desea firmar.

 #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); } 

Puede ver cómo se ve una tecla der en la estructura ASN1 en esta página web: https://lapo.it/asn1js/

Aquí hay un código de la biblioteca SwCrypt , que elimina el encabezado PKCS8 de una key privada. Esto es Swift, pero puede reescribirlo fácilmente en cualquier otro idioma.

  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)) }