原理
类似HTTPS协议传输原理,客户端用RSA的公钥加密AES的密钥,服务端用私钥解开获得的AES的密钥,客户端再与服务端进行AES加密的数据传输。
RSA算法原理:
- 找出两个“很大”的质数:P & Q(上百位)
N = P * Q
M = (P – 1) * (Q – 1) - 找出整数E,E与M互质,即除了1之外,没有其他公约数
- 找出整数D,使得 ED 除以 M 余 1,即 (E * D) % M = 1
- 经过上述准备工作之后,可以得到:E是公钥,负责加密D是私钥,负责解密N负责公钥和私钥之间的联系
- 加密算法,假定对X进行加密(X ^ E) % N = Y(6)解密算法,根据费尔马小定义,可以使用以下公式完成解密(Y ^ D) % N = X
公钥、私钥生成
公钥:就是签名机构签完给我们颁发的,放在网站的根目录上,可以分发
私钥:一般保存在中心服务器
加密解密使用了两种文件 .p12是私钥 .der是公钥,终端命令生成步骤如下:
- 创建私钥,生成安全强度是512(也可以是1024)的RAS私钥,.pem是base64的证书文件
openssl genrsa -out private.pem 512
- 生成一个证书请求,生成证书请求文件.csr
openssl req -new -key private.pem -out rsacert.csr
终端提示如下:
- 国家名字、代码
- 省的名字
- 城市的名字
- 公司的名字
- 公司的单位
- 我的名字
- 电子邮件
-
以及两个附加信息可以跳过
image
-
签名,找证书颁发机构签名,证明证书合法有效的,也可以自签名一个证书
生成证书并签名,有效期10年,生成一个.crt的一个base64公钥文件
openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
由于iOS开发时使用的时候不能是base64的,必须解成二进制文件! -
解成.der公钥二进制文件,放程序做加密用
openssl x509 -outform der -in rsacert.crt -out rsacert.der
-
生成.p12二进制私钥文件
image
.pem 是base64的不能直接使用,必须导成.p12信息交换文件用来传递秘钥
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
输入一个导出密码(框架中loadPrivateKey:方法的password参数需要用的密码):
加密过程
- 加载RSA公钥
NSString *pubPath = [[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil];
ZDCryptorTools *tools = [[ZDCryptorTools alloc] init]; [tools loadPublicKeyWithFilePath:pubPath];
#pragma mark - RSA 加密/解密算法
+ (void)loadPublicKeyWithFilePath:(NSString *)filePath; {
NSAssert(filePath.length != 0, @"公钥路径为空");
// 删除当前公钥
if (_publicKeyRef) CFRelease(_publicKeyRef);
// 从一个 DER 表示的证书创建一个证书对象
NSData *certificateData = [NSData dataWithContentsOfFile:filePath];
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, @"信任评估失败");
// 评估之后返回公钥子证书
_publicKeyRef = SecTrustCopyPublicKey(trustRef);
NSAssert(_publicKeyRef != NULL, @"公钥创建失败");
if (certificateRef) CFRelease(certificateRef);
if (policyRef) CFRelease(policyRef);
if (trustRef) CFRelease(trustRef);
}
2、生成16位随机字符串,作为AES的密钥
NSString *signKey = [ZDCryptorTools randomStringWithLength:16];
///生成长度为len的随机字符串
- (NSString *)randomStringWithLength:(NSInteger)len {
NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=";
NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
for (NSInteger i = 0; i < len; i++) {
[randomString appendFormat: @"%C", [letters characterAtIndex: arc4random_uniform((int)[letters length])]];
}
return randomString;
}
3、使用RSA公钥加密AES的密钥,加密内容最大长度 117
NSString *result = [tools RSAEncryptString:signKey];
- (NSString *)RSAEncryptString:(NSString *)string {
NSData *cipher = [self RSAEncryptData:[string dataUsingEncoding:NSUTF8StringEncoding]];
return [cipher base64EncodedStringWithOptions:0];
}
4、使用AES密钥加密参数
NSString *aesString = [XHCryptorTools AESEncryptString:params keyString:signKey iv:[@"1234567812345678" dataUsingEncoding:NSUTF8StringEncoding]];
+ (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *result = [self AESEncryptData:data keyString:keyString iv:iv];
// BASE 64 编码
return [result base64EncodedStringWithOptions:0];
}
+ (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCEncrypt keyString:keyString iv:iv];
}
+ (NSData *)CCCryptData:(NSData *)data algorithm:(CCAlgorithm)algorithm operation:(CCOperation)operation keyString:(NSString *)keyString iv:(NSData *)iv {
int keySize = (algorithm == kCCAlgorithmAES) ? kCCKeySizeAES128 : kCCKeySizeDES;
int blockSize = (algorithm == kCCAlgorithmAES) ? kCCBlockSizeAES128: kCCBlockSizeDES;
// 设置密钥
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[keySize];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:keySize];
// 设置 IV 向量
uint8_t cIv[blockSize];
bzero(cIv, blockSize);
int option = kCCOptionPKCS7Padding | kCCOptionECBMode;
if (iv) {
[iv getBytes:cIv length:blockSize];
option = kCCOptionPKCS7Padding;
}
// 设置输出缓冲区
size_t bufferSize = [data length] + blockSize;
void *buffer = malloc(bufferSize);
// 加密或解密
size_t cryptorSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
algorithm,
option,
cKey,
keySize,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&cryptorSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:cryptorSize];
} else {
free(buffer);
NSLog(@"[错误] 加密或解密失败 | 状态编码: %d", cryptStatus);
}
return result;
}
5、请求接口数据
6、AES解密接口返回的数据
NSData *jsonData = [ZDCryptorTools AESDecryptString:responseObject[@"data"] keyString:signKey iv:[@"1234567812345678" dataUsingEncoding:NSUTF8StringEncoding]];
+ (NSData *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
// BASE 64 解码
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
NSData *result = [self AESDecryptData:data keyString:keyString iv:iv];
return result;
}
+ (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCDecrypt keyString:keyString iv:iv];
}
网友评论