RSA加密解密
X509 文件扩展名
首先我们要理解文件的扩展名代表什么。DER、PEM、CRT和CER这些扩展名经常令人困惑。很多人错误地认为这些扩展名可以互相代替。尽管的确有时候有些扩展名是可以互换的,但是最好你能确定证书是如何编码的,进而正确地标识它们。正确地标识证书有助于证书的管理。
编码 (也用于扩展名)
- .DER = 扩展名DER用于二进制DER编码的证书。这些证书也可以用CER或者CRT作为扩展名。比较合适的说法是“我有一个DER编码的证书”,而不是“我有一个DER证书”。
- .PEM = 扩展名PEM用于ASCII(Base64)编码的各种X.509 v3 证书。文件开始由一行"—– BEGIN …“开始。
常用的扩展名
- .CRT = 扩展名CRT用于证书。证书可以是DER编码,也可以是PEM编码。扩展名CER和CRT几乎是同义词。这种情况在各种unix/linux系统中很常见。
- CER = CRT证书的微软型式。可以用微软的工具把CRT文件转换为CER文件(CRT和CER必须是相同编码的,DER或者PEM)。扩展名为CER的文件可以被IE识别并作为命令调用微软的cryptoAPI(具体点就是rudll32.exe cryptext.dll, CyrptExtOpenCER),进而弹出一个对话框来导入并/或查看证书内容。
- .KEY = 扩展名KEY用于PCSK#8的公钥和私钥。这些公钥和私钥可以是DER编码或者PEM编码。
CRT文件和CER文件只有在使用相同编码的时候才可以安全地相互替代。
RSA 特点:用私钥加密的字符串只能用公钥进行解密(记住私钥加密的字符串不能用私钥解密)
RSA 特点:用公钥加密的字符串只能用私钥进行解密(记住公钥加密的字符串不能用公钥解密)
p12格式:
-
生成私钥文件
$ openssl genrsa -out private.pem 1024 -
- openssl:是一个自由的软件组织,专注做加密和解密的框架。
- genrsa:指定了生成了算法使用RSA
- -out:后面的参数表示生成的key的输入文件
- 1024:表示的是生成key的长度,单位字节(bits)
-
创建证书请求
$ openssl req -new -key private.pem -out rsacert.csr -
- 可以拿着这个文件去数字证书颁发机构(即CA)申请一个数字证书。CA会给你一个新的文件cacert.pem,那才是你的数字证书。(要收费的)
-
生成证书并签名,有效期10年
$ openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt -
- 509是一种非常通用的证书格式。
- 将用上面生成的密钥privkey.pem和rsacert.csr证书请求文件生成一个数字证书rsacert.crt。这个就是公钥
-
转换格式 将 PEM 格式文件 转换成 DER 格式
$ openssl x509 -outform der -in rsacert.crt -out rsacert.der -
- 在 iOS开发中,公钥是不能使用base64编码的,上面的命令是将公钥的base64编码字符串转换成二进制数据
-
导出 P12 文件
-
- 在iOS使用私钥不能直接使用,需要导出一个p12文件。下面命令就是将私钥文件导出为p12文件。
$ openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
上面的证书iOS端使用p12和der;pem格式生成:
MAC上生成公钥、私钥的方法,及使用
- 1.打开终端,切换到自己想输出的文件夹下
- 2.输入指令:
openssl
(openssl是生成各种秘钥的工具,mac已经嵌入 - 3.输入指令:
genrsa -out rsa_private_key.pem 1024
(生成私钥,java端使用的) - 4.输入指令:
rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
(生成公钥) - 5.输入指令:
pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
(私钥转格式,在ios端使用私钥解密时用这个私钥)
注意:在MAC上生成三个.pem格式的文件,一个公钥,两个私钥,都可以在终端通过指令vim xxx.pem 打开,里面是字符串,第三步生成的私钥是java端用来解密数据的,第五步转换格式的私钥iOS端可以用来调试公钥、私钥解密(因为私钥不留在客户端)
1.其中rsa_private_key.pem 和rsa_public_key.pem在iOS中以文件形式加载加密解密时使用。
2.其中pkcs8_rsa_private_key.pem 和rsa_public_key.pem在iOS中以字符串形式加载进行加密解密时使用。
也就是说在私钥在iOS端以字符串加载加密解密时所用的字符串必须进行pkcs8转码。
我自己也是初步了解加密知识,测试的时候是这样,也不知道正确与否
从网上找了一些加密方面的Demo,
1.公钥、私钥字符串本地存在的参考:http://www.jianshu.com/p/8fd8306a95d0
2.公钥、私钥文件形式存在本地参考:http://www.cnblogs.com/cocoajin/p/6183443.html
(由于看的博客比较多,上面两个文档找了好久才再次找到)
我把两个结合起来,写到一个工具类里面,可以选择性的根据文件和字符串秘钥进行加密解密:
核心代码:
.h代码:
#import <Foundation/Foundation.h> @interface SecKeyTools : NSObject /** 从x509 cer证书中读取公钥 */ + (SecKeyRef )publicKeyFromCer:(NSString *)cerFile; /** 从 p12 文件中读取私钥,一般p12都有密码 */ + (SecKeyRef )privateKeyFromP12:(NSString *)p12File password:(NSString *)pwd; /** iOS 10 上可用如下接口SecKeyCreateWithData 从pem文件中读取私钥或公钥 */ + (SecKeyRef )publicKeyFromPem:(NSString *)pemFile keySize:(size_t )size; + (SecKeyRef )privaKeyFromPem:(NSString *)pemFile keySize:(size_t )size; /* 使用公钥私钥字符串加密解密,注意:私钥字符串需要进行pkcs8转码 */ + (SecKeyRef)PublicKey:(NSString *)key; + (SecKeyRef)PrivateKey:(NSString *)key; @end
.m代码:
#import "SecKeyTools.h" @implementation SecKeyTools /** 从x509 cer证书中读取公钥 */ + (SecKeyRef )publicKeyFromCer:(NSString *)cerFile { OSStatus err; NSData * certData; SecCertificateRef cert; SecPolicyRef policy; SecTrustRef trust; SecTrustResultType trustResult; SecKeyRef publicKeyRef; certData = [NSData dataWithContentsOfFile:cerFile]; cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData); policy = SecPolicyCreateBasicX509(); err = SecTrustCreateWithCertificates(cert, policy, &trust); NSAssert(err==errSecSuccess,@"证书加载失败"); err = SecTrustEvaluate(trust, &trustResult); NSAssert(err==errSecSuccess,@"公钥加载失败"); publicKeyRef = SecTrustCopyPublicKey(trust); CFRelease(policy); CFRelease(cert); return publicKeyRef; } /** 从 p12 文件中读取私钥,一般p12都有密码 */ + (SecKeyRef )privateKeyFromP12:(NSString *)p12File password:(NSString *)pwd { NSData * pkcs12Data; CFArrayRef imported; NSDictionary * importedItem; SecIdentityRef identity; OSStatus err; SecKeyRef privateKeyRef; pkcs12Data = [NSData dataWithContentsOfFile:p12File]; err = SecPKCS12Import((__bridge CFDataRef)pkcs12Data,(__bridge CFDictionaryRef) @{(__bridge NSString *)kSecImportExportPassphrase:pwd}, &imported); NSAssert(err==errSecSuccess,@"p12加载失败"); importedItem = (__bridge NSDictionary *) CFArrayGetValueAtIndex(imported, 0); identity = (__bridge SecIdentityRef) importedItem[(__bridge NSString *) kSecImportItemIdentity]; err = SecIdentityCopyPrivateKey(identity, &privateKeyRef); NSAssert(err==errSecSuccess,@"私钥加载失败"); CFRelease(imported); return privateKeyRef; } + (SecKeyRef )publicKeyFromPem:(NSString *)pemFile keySize:(size_t )size { SecKeyRef pubkeyref; NSError *readFErr = nil; CFErrorRef errref = noErr; NSString *pemStr = [NSString stringWithContentsOfFile:pemFile encoding:NSASCIIStringEncoding error:&readFErr]; NSAssert(readFErr==nil, @"pem文件加载失败"); pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----BEGIN PUBLIC KEY-----" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\r" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\n" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----END PUBLIC KEY-----" withString:@""]; NSData *dataPubKey = [[NSData alloc]initWithBase64EncodedString:pemStr options:0]; NSMutableDictionary *dicPubkey = [[NSMutableDictionary alloc]initWithCapacity:1]; [dicPubkey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [dicPubkey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)kSecAttrKeyClass]; [dicPubkey setObject:@(size) forKey:(__bridge id)kSecAttrKeySizeInBits]; pubkeyref = SecKeyCreateWithData((__bridge CFDataRef)dataPubKey, (__bridge CFDictionaryRef)dicPubkey, &errref); NSAssert(errref==noErr, @"公钥加载错误"); return pubkeyref; } + (SecKeyRef )privaKeyFromPem:(NSString *)pemFile keySize:(size_t )size { SecKeyRef prikeyRef; NSError *readFErr = nil; CFErrorRef err = noErr; NSString *pemStr = [NSString stringWithContentsOfFile:pemFile encoding:NSASCIIStringEncoding error:&readFErr]; NSAssert(readFErr==nil, @"pem文件加载失败"); pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----BEGIN RSA PRIVATE KEY-----" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\r" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\n" withString:@""]; pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----END RSA PRIVATE KEY-----" withString:@""]; NSData *pemData = [[NSData alloc]initWithBase64EncodedString:pemStr options:0]; NSMutableDictionary *dicPrikey = [[NSMutableDictionary alloc]initWithCapacity:1]; [dicPrikey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [dicPrikey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)kSecAttrKeyClass]; [dicPrikey setObject:@(size) forKey:(__bridge id)kSecAttrKeySizeInBits]; prikeyRef = SecKeyCreateWithData((__bridge CFDataRef)pemData, (__bridge CFDictionaryRef)dicPrikey, &err); NSAssert(err==noErr, @"私钥加载错误"); return prikeyRef; } + (SecKeyRef)PublicKey:(NSString *)key{ NSRange spos = [key rangeOfString:@"-----BEGIN RSA PUBLIC KEY-----"]; NSRange epos = [key rangeOfString:@"-----END RSA PUBLIC KEY-----"]; if(spos.location != NSNotFound && epos.location != NSNotFound){ NSUInteger s = spos.location + spos.length; NSUInteger e = epos.location; NSRange range = NSMakeRange(s, e-s); key = [key substringWithRange:range]; } key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@" " withString:@""]; // NSLog(@"key is ...%@\n",key); // This will be base64 encoded, decode it. NSData *data = [[NSData alloc]initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters]; data = [SecKeyTools stripPublicKeyHeader:data]; if(!data){ return nil; } //a tag to read/write keychain storage NSString *tag = @"RSAUtil_PubKey"; NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; // Delete any old lingering key with the same tag NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init]; [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass]; [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag]; SecItemDelete((__bridge CFDictionaryRef)publicKey); // Add persistent version of the key to system keychain [publicKey setObject:data forKey:(__bridge id)kSecValueData]; [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id) kSecAttrKeyClass]; [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnPersistentRef]; CFTypeRef persistKey = nil; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey); if (persistKey != nil){ CFRelease(persistKey); } if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; } [publicKey removeObjectForKey:(__bridge id)kSecValueData]; [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef]; [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef]; [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; // Now fetch the SecKeyRef version of the key SecKeyRef keyRef = nil; status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef); if(status != noErr){ return nil; } return keyRef; } + (SecKeyRef)PrivateKey:(NSString *)key{ NSRange spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"]; NSRange epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"]; if(spos.location != NSNotFound && epos.location != NSNotFound){ NSUInteger s = spos.location + spos.length; NSUInteger e = epos.location; NSRange range = NSMakeRange(s, e-s); key = [key substringWithRange:range]; } key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""]; key = [key stringByReplacingOccurrencesOfString:@" " withString:@""]; // This will be base64 encoded, decode it. NSData *data = [[NSData alloc]initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters]; data = [SecKeyTools stripPrivateKeyHeader:data]; if(!data){ return nil; } //a tag to read/write keychain storage NSString *tag = @"RSAUtil_PrivKey"; NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; // Delete any old lingering key with the same tag NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init]; [privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass]; [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag]; SecItemDelete((__bridge CFDictionaryRef)privateKey); // Add persistent version of the key to system keychain [privateKey setObject:data forKey:(__bridge id)kSecValueData]; [privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id) kSecAttrKeyClass]; [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnPersistentRef]; CFTypeRef persistKey = nil; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey); if (persistKey != nil){ CFRelease(persistKey); } if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; } [privateKey removeObjectForKey:(__bridge id)kSecValueData]; [privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef]; [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef]; [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; // Now fetch the SecKeyRef version of the key SecKeyRef keyRef = nil; status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef); if(status != noErr){ return nil; } return keyRef; } + (NSData *)stripPublicKeyHeader:(NSData *)d_key{ // Skip ASN.1 public key header if (d_key == nil) return(nil); unsigned long len = [d_key length]; if (!len) return(nil); unsigned char *c_key = (unsigned char *)[d_key bytes]; unsigned int idx = 0; if (c_key[idx++] != 0x30) return(nil); if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; else idx++; // PKCS #1 rsaEncryption szOID_RSA_RSA static unsigned char seqiod[] = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00 }; if (memcmp(&c_key[idx], seqiod, 15)) return(nil); idx += 15; if (c_key[idx++] != 0x03) return(nil); if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1; else idx++; if (c_key[idx++] != '\0') return(nil); // Now make a new NSData from this buffer return([NSData dataWithBytes:&c_key[idx] length:len - idx]); } //credit: http://hg.mozilla.org/services/fx-home/file/tip/Sources/NetworkAndStorage/CryptoUtils.m#l1036 + (NSData *)stripPrivateKeyHeader:(NSData *)d_key{ // Skip ASN.1 private key header if (d_key == nil) return(nil); unsigned long len = [d_key length]; if (!len) return(nil); unsigned char *c_key = (unsigned char *)[d_key bytes]; unsigned int idx = 22; //magic byte at offset 22 if (0x04 != c_key[idx++]) return nil; //calculate length of the key unsigned int c_len = c_key[idx++]; int det = c_len & 0x80; if (!det) { c_len = c_len & 0x7f; } else { int byteCount = c_len & 0x7f; if (byteCount + idx > len) { //rsa length field longer than buffer return nil; } unsigned int accum = 0; unsigned char *ptr = &c_key[idx]; idx += byteCount; while (byteCount) { accum = (accum << 8) + *ptr; ptr++; byteCount--; } c_len = accum; } // Now make a new NSData from this buffer return [d_key subdataWithRange:NSMakeRange(idx, c_len)]; } @end
初探佳境,多多学习交流