前言
接着上篇05-密码学(1)的RSA算法,最后我们介绍了下签名与验证,也就是私钥加密,公钥解密,对应的实际运用场景,就是我们熟知的iOS系统证书的申请流程。本篇文章会结合RSA算法分析下证书的整个申请流程,以及RSA代码演示。
一、证书
上篇05-密码学(1)中,我们介绍了Mac系统内置OpenSSL(开源加密库)可以帮助我们生成公钥私钥完成加解密,但是我们在实际的开发中,是没有办法直接使用.pem文件进行加解密,而是Xcode帮我们去钥匙串访问申请的证书,我们通过这个证书与苹果的服务器进行验签等交互。
1.1证书生成
在证书生成之前,我们需要生成一个请求文件 👉 CertificateSigningRequest.certSigningRequest(简称csr文件),有2种方式👇
- 钥匙串生成
csr文件 - 命令行生成
csr文件
钥匙串生成
操作路径 👉 钥匙串 ->钥匙串访问->证书助理->从证书颁发机构请求证书,如下图👇
接着填入基本信息, 然后创建👇
这个时候就生成了一个请求csr文件。
命令行生成
再看看命令行是如何操作的。
- 通过私钥生成csr文件
openssl req -new -key private.pem -out rsacer.csr
利用上篇文章中生成的密钥.pem文件,生成csr请求文件👇
在这里通过自己创建的私钥生成了请求文件。将请求文件发送到签名机构进行签名(⚠️需要收费)。
自己通过csr生成签名证书
当然,我们也可以不通过签名机构进行签名,直接自己签名生成crt证书。
⚠️注意:通过
私钥自己签名生成证书(crt)(这里是没有认证的)。
命令行👇
openssl x509 -req -days 3650 -in rsacer.csr -signkey private.pem -out rsacert.crt
这个crt证书就类似公司服务器上的证书,给别人接收的。但是这个时候crt证书仍然用不了,需要转换为der👇
openssl x509 -outform der -in rsacert.crt -out rsacert.der
因为从苹果申请的证书就是der证书。
p12
还有一个我们熟悉的,p12格式文件,怎么生成呢?
- 从钥匙串中导出👇
- 命令行操作
从crt获取p12文件👇
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
⚠️注意:
p12(私钥)和der(证书)是一对。iOS开发中就是使用这两个进行加密和解密,是不是和RSA的公私钥原理一模一样。。。
二、RSA代码演示
接下来,我们接着之前的RSA算法,用代码演示一下加解密的过程。
2.1 Base64编解码
在演示之前,我们先来看看什么是Base64?代码加解密的过程中经常会用到Base64,为什么要Base64编解码呢?因为加密解密完数据都是二进制格式,一般情况下转换为base64是为了方便查看。
- base64编码
base64 message.txt -o test.txt
编码前,message.txt的内容👇
编码后,test.txt的内容👇
编码后变成了5a+G56CBOkxHUGVyc29uCg==
base64编码由
0-9 a-z A-Z / =64个字符组成。
- base64解码
base64 test.txt -o message2.txt -D
这里就不再示例演示了,和编码是一个道理,读者可自行敲一遍看看。
base64编码规则
例如:Man的编码对应如下👇
上图中可看出👇
base64按照
6个二进制编码
有表可查👇
该表相当于密码本。由于通过6个二进制位来编码一个结果,如果要编码的字节数不能被3整除,最后会多出1个或2个字节(补0)。例如👇
补的
0变成了=
在还原的时候通过查表可以找到索引,索引能对应到二进制位数据,就可以还原数据了。
iOS中的Base64
iOS系统本身是支持base64的编解码的👇
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"base64 encode:%@",[self base64Encode:@"LGPerson"]);
NSLog(@"base64 decode:%@",[self base64Decode:@"TEdQZXJzb24="]);
}
- (NSString *)base64Encode:(NSString *)message {
NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
return [messageData base64EncodedStringWithOptions:0];
}
- (NSString *)base64Decode:(NSString *)base64Message {
NSData *base64Data = [[NSData alloc] initWithBase64EncodedString:base64Message options:0];
return [[NSString alloc] initWithData:base64Data encoding:NSUTF8StringEncoding];
}
运行👇
base64是以查表的形式对二进制进行编码,只适用于二进制文件。编码后文件会变大,会增加原来的1/3。
2.2 RSA代码
iOS本身支持RSA,系统提供了SecKeyEncrypt加密和SecKeyDecrypt解密 函数。具体的定义在Security系统库中。
- 加载公钥&私钥
//1.加载公钥
[[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
//2.加载私钥
[[RSACryptor sharedRSACryptor] loadPrivateKey: [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];
- 加密&解密
// 加密
NSData * result = [[RSACryptor sharedRSACryptor] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
//base64编码
NSString * base64 = [result base64EncodedStringWithOptions:0];
NSLog(@"加密之后:%@\n",base64);
//解密
NSData * dcStr = [[RSACryptor sharedRSACryptor] decryptData:result];
NSLog(@"解密之后:%@",[[NSString alloc] initWithData:dcStr encoding:NSUTF8StringEncoding]);
再运行一次👇
可以发现,每次加密后的base64结果都不同,解密后相同。这个原因是rsa内部实现的填充模式导致的。
具体SecKeyEncrypt函数第二个参数SecPadding中,👇
/*!
@typedef SecPadding
@abstract Supported padding types.
*/
typedef CF_OPTIONS(uint32_t, SecPadding)
{
kSecPaddingNone = 0,
kSecPaddingPKCS1 = 1,
kSecPaddingOAEP = 2, // __OSX_UNAVAILABLE __IOS_AVAILABLE(2.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0),
/* For SecKeyRawSign/SecKeyRawVerify only,
ECDSA signature is raw byte format {r,s}, big endian.
First half is r, second half is s */
kSecPaddingSigRaw = 0x4000,
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is an MD2
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1MD2 = 0x8000, // __OSX_DEPRECATED(10.0, 10.12, "MD2 is deprecated") __IOS_DEPRECATED(2.0, 5.0, "MD2 is deprecated") __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE,
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is an MD5
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1MD5 = 0x8001, // __OSX_DEPRECATED(10.0, 10.12, "MD5 is deprecated") __IOS_DEPRECATED(2.0, 5.0, "MD5 is deprecated") __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE,
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is a SHA1
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1SHA1 = 0x8002,
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is a SHA224
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1SHA224 = 0x8003, // __OSX_UNAVAILABLE __IOS_AVAILABLE(2.0),
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is a SHA256
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1SHA256 = 0x8004, // __OSX_UNAVAILABLE __IOS_AVAILABLE(2.0),
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is a SHA384
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1SHA384 = 0x8005, // __OSX_UNAVAILABLE __IOS_AVAILABLE(2.0),
/* For SecKeyRawSign/SecKeyRawVerify only, data to be signed is a SHA512
hash; standard ASN.1 padding will be done, as well as PKCS1 padding
of the underlying RSA operation. */
kSecPaddingPKCS1SHA512 = 0x8006, // __OSX_UNAVAILABLE __IOS_AVAILABLE(2.0),
};
SecPadding改为kSecPaddingNone则密文每次都一样了。
加解密Demo
总结
-
证书
csr请求文件,2种方式申请钥匙串 ->钥匙串访问->证书助理->从证书颁发机构请求证书- 终端命令行
openssl req -new -key private.pem -out rsacer.csr
crt证书生成- 将
csr请求文件发送到签名机构进行签名(⚠️需要收费) - 自己签名生成
证书(crt)(这里是没有认证的)- 2.1
openssl x509 -req -days 3650 -in rsacer.csr -signkey private.pem -out rsacert.crt - 2.2 从
crt获取p12文件👉openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
- 2.1
- 将
p12- 从
钥匙串导出 - 命令行操作 👉 openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
- 从
p12(私钥)和der(证书)是一对。iOS开发中就是使用这两个进行加密和解密。
-
RSA代码演示
- Base64
- 由
0-9 a-z A-Z / =64个字符组成 - 按照
6个二进制编码,位数多的补0 - iOS中的Base64
- 由
- Base64
- 编码 👉
NSData的方法👇- (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));
- 解码 👉
NSString的方法👇- (nullable instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
- RSA代码
-
iOS本身支持RSA,系统提供了
SecKeyEncrypt和SecKeyDecrypt加密和解密函数 -
RSA的
安全系数非常高(因为整个业务逻辑非常安全) -
加密
效率低(不能用于大数据加密) -
用来加密
关键数据
-