在运行时,我的iOS应用程序收到一个文件,其中包含由其他人的Java生成的公私RSA密钥对:
KeyPairGenerator keygenerator;
keygenerator = KeyPairGenerator.getInstance("RSA");
keygenerator.initialize(4096);
KeyPair keypair = keygenerator.generateKeyPair();
PrivateKey privateKey = keypair.getPrivate().getEncoded();
PublicKey publicKey = keypair.getPublic().getEncoded();
我已成功阅读并使用了 public 键,使用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 :(得分: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);
}
答案 1 :(得分:0)
您可以在此网页中查看按键在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))
}