目录
简介
目前主流的加密算法分为两种:对称秘钥加密和非对称秘钥加密。
对称秘钥加密主要包括:DES 3DES AES
非对称秘钥加密又叫公开秘钥加密主要包括:RSA
对称秘钥加密:需要发送方与接收方知 道共享的秘密密钥,最初如何商定密钥( 尤其“素未谋面”)?
该篇主要讲解非对称秘钥加密,对称秘钥加密见上篇
公开秘钥加密:完全不同的方法 [Diffie-Hellman76, RSA78],解决了秘钥传输问题
- 发送方与接收方无需共享秘密密钥
- 公开密钥(公钥)完全公开
- 私有密钥(私钥)只有接收方知道
原理
需求:
- 公钥加密 KB+ 和私钥解密KB- 需要满足:KB- ( KB+(m) ) = m
- 给定公钥 KB+ 不可能计算得到私钥 KB-
前提条件: 模运算
x mod n = x除以n的余数
事实上:
[(a mod n) + (b mod n)] mod n = (a+b) mod n
[(a mod n) - (b mod n)] mod n = (a-b) mod n
[(a mod n) * (b mod n)] mod n = (a*b) mod n
因此:
(a mod n)d次方 mod n = a的d次方 mod n
例如:x=14, n=10, d=2,则
(x mod n)d次方 mod n = (4*4) mod 10 = 6
x的d次方 = 14*14 = 196
x的d次方 mod 10 = 6
预备知识
报文/信息(message): 仅仅是一个比特模式,每个比特模式可以表示为一个唯一的整数,因此,加密一个报文就等价于加密一个数例如:
m= 10010001,可以唯一地表示为十进制 数145
为了加密m,我们可以加密对应的数(145) ,得到一个新的数(即密文)
生成公钥/私钥对
- 选择2个大质数p和q。(e.g., 1024bits的大质数)
- 计算n = pq,z = (p-1)(q-1)
- 选择e满足e <n 使e与z 之间没有公因子,即e,z互质(relatively prime)
- 选择d使得 e的d次方-1 刚好可以被z整除, (即:e的d次方modz =1).
- 公钥: (n,e);私钥:(n,d).
加密、解密
- 给定公钥 (n,e)和私钥(n,d)
- 加密报文m (m<n)时,计算 c = m的e次方 mod n
- 解密密文c时,计算 m = c的d次方 mod n
m=(m的e次方mod n)d次方 mod n
举例
理论依据
另一个重要性质
安全性
RSA的安全性建立在“大数分解和素性检测”这个数论难题的基础上
既将两个大素数相乘在计算上容易实现,而将该乘积分解的计算量相当大
假设已知Bob的公钥(n,e),那么有多大难 度确定d,即私钥(n,d)?
本质上需要在不知道两个因子p和q的前提 下,找出n的因子分解一个大数是很困难的!
RSA的幂运算强度很大 DES至少比RSA快100倍。实际应用中:利用公钥加密建立安全连接,然后建立第二个密钥-对称会话密钥,用于加密数据
会话密钥(session key, KS)
Bob与Alice利用RSA交换对称会话密钥KS
一旦双方确认KS,则利用会话密钥加密/解密会话数据
代码实现
LCRSA2.h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LCRSA2 : NSObject
/// 公钥加密
/// @param plainData 要加密的数据
/// @param publicKey 公钥字符串
+(NSData*)encryptData:(NSData*)plainData
publicKey:(NSString*)publicKey;
/// 私钥解密
/// @param cipherData 要解密的数据
/// @param privateKey 私钥字符串
+(NSData *)decryptData:(NSData *)cipherData
privateKey:(NSString*)privateKey;
/// 公钥加密
/// @param plainData 要加密的数据
/// @param publicKeyPath 证书路径
+(NSData*)encryptData:(NSData*)plainData
publicKeyPath:(NSString*)publicKeyPath;
/// 私钥解密
/// @param cipherData 要解密的数据
/// @param privateKeyPath p12文件路径
+(NSData *)decryptData:(NSData *)cipherData
privateKeyPath:(NSString*)privateKeyPath;
@end
NS_ASSUME_NONNULL_END
LCRSA2.m文件
#import "LCRSA2.h"
@implementation LCRSA2
+(NSData*)encryptData:(NSData*)plainData
publicKey:(NSString*)publicKey{
SecKeyRef publicKeyRef = [self __getPublicKeyRef:publicKey];
return [LCRSA2 __encryptData:plainData publicKeyRef:publicKeyRef padding:kSecPaddingPKCS1];
}
+(NSData *)decryptData:(NSData *)cipherData
privateKey:(NSString*)privateKey{
SecKeyRef privateKeyRef = [self __getPrivateKeyRef:privateKey];
return [LCRSA2 __decryptData:cipherData privateKeyRef:privateKeyRef padding:kSecPaddingPKCS1];
}
+(NSData*)encryptData:(NSData*)plainData
publicKeyPath:(NSString*)publicKeyPath{
SecKeyRef publicKeyRef = [self __loadPublicKeyRef:publicKeyPath];
return [LCRSA2 __encryptData:plainData publicKeyRef:publicKeyRef padding:kSecPaddingPKCS1];
}
+(NSData *)decryptData:(NSData *)cipherData
privateKeyPath:(NSString*)privateKeyPath{
SecKeyRef privateKeyRef = [self __loadPrivateKey:privateKeyPath password:@"123456"];
return [LCRSA2 __decryptData:cipherData privateKeyRef:privateKeyRef padding:kSecPaddingPKCS1];
}
#pragma mark - 内部方法
// !!!: 公钥加密、解密
+(NSData *)__encryptData:(NSData *)plainData
publicKeyRef:(SecKeyRef)publicKeyRef
padding:(SecPadding)padding{
OSStatus sanityCheck = noErr;
size_t cipherBufferSize = 0;
size_t keyBufferSize = 0;
NSAssert(plainData != nil, @"明文数据为空");
NSAssert(publicKeyRef != nil, @"公钥为空");
NSData *cipher = nil;
uint8_t *cipherBuffer = NULL;
// 计算缓冲区大小
cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
keyBufferSize = [plainData length];
//填充模式
//kSecPaddingNone 每次加密结果是固定的,
//kSecPaddingPKCS1是随机变化的
if (padding == kSecPaddingNone) {
NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大");
} else {
NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大");
}
// 分配缓冲区
cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
// 使用公钥加密
sanityCheck = SecKeyEncrypt(publicKeyRef,
padding,
(const uint8_t *)[plainData bytes],
keyBufferSize,
cipherBuffer,
&cipherBufferSize
);
NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %dd", (int)sanityCheck);
// 生成密文数据
cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
if (cipherBuffer) free(cipherBuffer);
return cipher;
}
+(NSData *)__decryptData:(NSData *)cipherData
privateKeyRef:(SecKeyRef)privateKeyRef
padding:(SecPadding)padding{
OSStatus sanityCheck = noErr;
size_t cipherBufferSize = 0;
size_t keyBufferSize = 0;
NSData *key = nil;
uint8_t *keyBuffer = NULL;
NSAssert(privateKeyRef != NULL, @"私钥不存在");
// 计算缓冲区大小
cipherBufferSize = SecKeyGetBlockSize(privateKeyRef);
keyBufferSize = [cipherData length];
NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大");
// 分配缓冲区
keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
memset((void *)keyBuffer, 0x0, keyBufferSize);
// 使用私钥解密
sanityCheck = SecKeyDecrypt(privateKeyRef,
padding,
(const uint8_t *)[cipherData bytes],
cipherBufferSize,
keyBuffer,
&keyBufferSize
);
NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck);
// 生成明文数据
key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
if (keyBuffer) free(keyBuffer);
return key;
}
// !!!: 从证书提取SecKeyRef
+(SecKeyRef)__loadPublicKeyRef:(NSString *)publicKeyPath {
NSAssert(publicKeyPath.length != 0, @"公钥路径为空");
// 从一个 DER 表示的证书创建一个证书对象
NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
NSAssert(certificateRef != NULL, @"公钥文件错误");
// 返回一个默认 X509 策略的公钥对象,使用之后需要调用 CFRelease 释放
SecPolicyRef policyRef = SecPolicyCreateBasicX509();
// 包含信任管理信息的结构体
SecTrustRef trustRef;
// 基于证书和策略创建一个信任管理对象
OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
NSAssert(status == errSecSuccess, @"创建信任管理对象失败");
// 信任结果
SecTrustResultType trustResult;
// 评估指定证书和策略的信任管理是否有效
status = SecTrustEvaluate(trustRef, &trustResult);
NSAssert(status == errSecSuccess, @"信任评估失败");
// 评估之后返回公钥子证书
SecKeyRef publicKeyRef = SecTrustCopyPublicKey(trustRef);
NSAssert(publicKeyRef != NULL, @"公钥创建失败");
if (certificateRef) CFRelease(certificateRef);
if (policyRef) CFRelease(policyRef);
if (trustRef) CFRelease(trustRef);
return publicKeyRef;
}
+(SecKeyRef)__loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
NSAssert(privateKeyPath.length != 0, @"私钥路径为空");
NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef passwordRef = (__bridge CFStringRef)password;
// 从 PKCS #12 证书中提取标示和证书
SecIdentityRef myIdentity;
SecTrustRef myTrust;
const void *keys[] = {kSecImportExportPassphrase};
const void *values[] = {passwordRef};
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
// 返回 PKCS #12 格式数据中的标示和证书
OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (status == noErr) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
}
if (optionsDictionary) CFRelease(optionsDictionary);
NSAssert(status == noErr, @"提取身份和信任失败");
SecTrustResultType trustResult;
// 评估指定证书和策略的信任管理是否有效
status = SecTrustEvaluate(myTrust, &trustResult);
NSAssert(status == errSecSuccess, @"信任评估失败");
SecKeyRef privateKeyRef = nil;
// 提取私钥
status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
NSAssert(status == errSecSuccess, @"私钥创建失败");
return privateKeyRef;
}
// !!!: 从公私钥字符串内提取SecKeyRef
+ (SecKeyRef)__getPublicKeyRef:(NSString *)key{
NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];
NSRange epos = [key rangeOfString:@"-----END 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:@""];
// This will be base64 encoded, decode it.
NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
data = [LCRSA2 __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)__getPrivateKeyRef:(NSString *)key{
NSRange spos;
NSRange epos;
spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"];
if(spos.length > 0){
epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"];
}else{
spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"];
epos = [key rangeOfString:@"-----END 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 = [LCRSA2 __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]);
}
+ (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
行者常至,为者常成!