01:RSA 加密和原理 .pem .csr .crt .der .p12文件的区别 base64
复习:
1:rsa加密解密6个步骤,p、q、n、φ(n)、e、d
2: 加密理论:欧拉函数、欧拉定理、模反元素、迪菲赫尔曼秘钥交换、RSA加解密
3:生成证书的步骤、证书的关系。
4:base64编解码的目的:对于二进制数据不便于查看或者表示,所以用base64来编码查看。cat、xxd命令
5:rsa填充模式:128字节-明文117字节。pkcs1
6:RSA用途及特点
特点:RSA加密相对比较安全,但是通过数学算法来加密和解密,效率比较低。
用途:所以一般RSA的主战场是加密比较小的数据,比如对大数据进行对称加密,再用RSA给对称加密的KEY进行加密,或者加密Hash值,也就是数字签名。
目录
一:RSA加密原理
二:RSA加密理论
三:终端openssl 公钥加密,私钥解密,私钥签名,公钥验证
四:生成证书 csr、crt、der公钥证书、p12私钥
五:base64编解码
六:RSA代码
七:base64代码
正文-RSA
一:RSA加密的原理:
1:第一步,随机选择两个不相等的质数p和q。
p=61和q=53。(实际应用中,这两个质数越大,就越难破解。)
2:计算p和q的乘积n。
n = 61×53 = 3233
n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。
第三步,计算n的欧拉函数φ(n)。
φ(n) = (p-1)(q-1)
φ(3233)等于60×52,即3120。
第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。
e=17
第五步,计算e对于φ(n)的模反元素d。
ed - 1 = kφ(n)
d=2753。
第六步,将n和e封装成公钥,n和d封装成私钥。
二:理论知识:
1: 模反元素:
如果两个正整数a和n互质,那么一定可以找到整数b,使得( a乘以b)-1 被n整除,或者说ab被n除的余数是1。
(a乘以b)-1 = kn.
2: 欧拉函数:计算φ(n)。
任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?)计算这个值的方法就叫做欧拉函数,以φ(n)表示。
2.1:n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),
则φ(n) = φ(p^k) = p^k - p^(k-1)。也就是φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4
2.2:如果n是质数,则 φ(n)=n-1 。
计算7的欧拉函数,和7互质的 1、2、3、4、5、6、7
φ(7) = 6
2.3:如果n可以分解成两个互质的整数之积,即 n = p * k ,则φ(n) = φ(p * k) = φ(p)*φ(k).
φ(56) = φ(8) * φ(7) = 4 * 6 = 24
3:欧拉定理:
如果两个正整数m和n互质,那么m的φ(n)次方减去1,可以被n整除。
4:迪菲赫尔曼秘钥交换
5:加密解密
m<n.
三:使用rsa加密解密,签名,认证
1:生成RSA私钥,base64编码的二进制数据。
openssl genrsa -out private.pem 1024
生成一个1024位的私钥
2:从private.pem 私钥中提取公钥,输出
openssl rsa -in private.pem -pubout -out public.pem
3:查看文件内容
cat private.pem
cat public.pem
pem文件中都是base64编码的二进制数据。
4:将私钥转化成明文
将私钥转化成明文
openssl rsa -in private.pem -text -out private.txt
5: 通过公钥rsa加密message.txt
openssl rsautl -encrypt -in message.txt -inkey public.pem -pubin -out enc.txt
6:通过私钥解密enc.txt
openssl rsautl -decrypt -in enc.txt -inkey private.pem -out dec.txt
7:使用私钥签名
openssl rsautl -sign -in message.txt -inkey private.pem -out enc.bin
8:xxd查看二进制文件,
9:使用公钥认证
openssl rsautl -verify -in enc.bin -inkey public.pem -pubin -out decode.txt
四:使用openssl生成证书
1:从代码中不会使用直接使用pem,会从pem中提取证书。 需要输入一些证书请求信息,参考提示内容。
openssl req -new -key private.pem -out rsacert.csr
csr文件不是证书,是请求证书文件。
2: 给请求文件 从 pem文件中 提取证书,.crt 这个证书还是base64编码的,这个crt文件也不是直接使用的。
签名一个证书一年5千元。
openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
3:生成der文件. 这个der文件主要包含公钥。最后通过这个来生成一个p12是这个der对应的私钥文件。
openssl x509 -outform der -in rsacert.crt -out rsacert.der
4:通过crt文件通提取私钥P12文件,公钥也是从crt中提取出来的
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
5:证书的关系
1:der 文件是公钥
.p12 是私钥文件
这两个是二进制文件
2:他们是从crt文件中提取出来的,crt文件是从pem文件中提取出来的。
crt和pem中有公钥和私钥他们是base64编码的文件。
五:base64编解码,所有的base64都是对二进制数据操作的。
A-Z 26个大写
a-z 26个小写
0-9 10个字母
/ + =(不足6位的用等号补充)
等号只会出现在最后面,
对于二进制数据不便于查看或者表示,所以用base64来编码查看。
1:base64编码
base64 zk.png -o zk.txt
2:base64解码
base64 zk.txt -o zk.png -D
六:rsa加密的代码
rsa加密的是二进制数据。
1:加密
rsa加密的是二进制数据
加密后的数据用base64来表示。表示结果是字符串。
2:解密
解密的是二进制数据。
3:填充模式
不填充
pkcs 填充模式,加密数据是随机的。
代码
1:RSA加密代码
#import <Foundation/Foundation.h> @interface RSACryptor : NSObject + (instancetype)sharedRSACryptor; /** * 生成密钥对 * * @param keySize 密钥尺寸,可选数值(512/1024/2048) */ - (void)generateKeyPair:(NSUInteger)keySize; /** * 加载公钥 * * @param publicKeyPath 公钥路径 * @code # 生成证书 $ openssl genrsa -out ca.key 1024 # 创建证书请求 $ openssl req -new -key ca.key -out rsacert.csr # 生成证书并签名 $ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt # 转换格式 $ openssl x509 -outform der -in rsacert.crt -out rsacert.der @endcode */ - (void)loadPublicKey:(NSString *)publicKeyPath; /** * 加载私钥 * * @param privateKeyPath p12文件路径 * @param password p12文件密码 * @code openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt @endcode */ - (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password; /** * 加密数据 * * @param plainData 明文数据 * * @return 密文数据 */ - (NSData *)encryptData:(NSData *)plainData; /** * 解密数据 * * @param cipherData 密文数据 * * @return 明文数据 */ - (NSData *)decryptData:(NSData *)cipherData; @end
#import "RSACryptor.h" // 填充模式 #define kTypeOfWrapPadding kSecPaddingPKCS1 // 公钥/私钥标签 #define kPublicKeyTag "com.zk.publickey" #define kPrivateKeyTag "com.zk.privatekey" static const uint8_t publicKeyIdentifier[] = kPublicKeyTag; static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag; @interface RSACryptor() { SecKeyRef publicKeyRef; // 公钥引用 SecKeyRef privateKeyRef; // 私钥引用 } @property (nonatomic, retain) NSData *publicTag; // 公钥标签 @property (nonatomic, retain) NSData *privateTag; // 私钥标签 @end @implementation RSACryptor + (instancetype)sharedRSACryptor { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { // 查询密钥的标签 _privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)]; _publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)]; } return self; } #pragma mark - 加密 & 解密数据 - (NSData *)encryptData:(NSData *)plainData { 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]; if (kTypeOfWrapPadding == kSecPaddingNone) { NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大"); } else { NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大"); } // 分配缓冲区 cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t)); memset((void *)cipherBuffer, 0x0, cipherBufferSize); // 使用公钥加密 sanityCheck = SecKeyEncrypt(publicKeyRef, kTypeOfWrapPadding, (const uint8_t *)[plainData bytes], keyBufferSize, cipherBuffer, &cipherBufferSize ); NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %d", sanityCheck); // 生成密文数据 cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize]; if (cipherBuffer) free(cipherBuffer); return cipher; } - (NSData *)decryptData:(NSData *)cipherData { OSStatus sanityCheck = noErr; size_t cipherBufferSize = 0; size_t keyBufferSize = 0; NSData *key = nil; uint8_t *keyBuffer = NULL; SecKeyRef privateKey = NULL; privateKey = [self getPrivateKeyRef]; NSAssert(privateKey != NULL, @"私钥不存在"); // 计算缓冲区大小 cipherBufferSize = SecKeyGetBlockSize(privateKey); keyBufferSize = [cipherData length]; NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大"); // 分配缓冲区 keyBuffer = malloc(keyBufferSize * sizeof(uint8_t)); memset((void *)keyBuffer, 0x0, keyBufferSize); // 使用私钥解密 sanityCheck = SecKeyDecrypt(privateKey, kTypeOfWrapPadding, (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; } #pragma mark - 密钥处理 /** * 生成密钥对 */ - (void)generateKeyPair:(NSUInteger)keySize { OSStatus sanityCheck = noErr; publicKeyRef = NULL; privateKeyRef = NULL; NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密钥尺寸无效 %tu", keySize); // 删除当前密钥对 [self deleteAsymmetricKeys]; // 容器字典 NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init]; NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init]; NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init]; // 设置密钥对的顶级字典 [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits]; // 设置私钥字典 [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent]; [privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag]; // 设置公钥字典 [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent]; [publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag]; // 设置顶级字典属性 [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs]; [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs]; // SecKeyGeneratePair 返回密钥对引用 sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef); NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密钥对失败"); } /** * 加载公钥 */ - (void)loadPublicKey:(NSString *)publicKeyPath { NSAssert(publicKeyPath.length != 0, @"公钥路径为空"); // 删除当前公钥 if (publicKeyRef) CFRelease(publicKeyRef); // 从一个 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, @"信任评估失败"); // 评估之后返回公钥子证书 publicKeyRef = SecTrustCopyPublicKey(trustRef); NSAssert(publicKeyRef != NULL, @"公钥创建失败"); if (certificateRef) CFRelease(certificateRef); if (policyRef) CFRelease(policyRef); if (trustRef) CFRelease(trustRef); } /** * 加载私钥 */ - (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password { NSAssert(privateKeyPath.length != 0, @"私钥路径为空"); // 删除当前私钥 if (privateKeyRef) CFRelease(privateKeyRef); NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath]; CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data; CFStringRef passwordRef = (__bridge CFStringRef)password; // 从 PKCS #12 证书中提取标示和证书 SecIdentityRef myIdentity = NULL; SecTrustRef myTrust = NULL; 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, @"信任评估失败"); // 提取私钥 status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef); NSAssert(status == errSecSuccess, @"私钥创建失败"); } /** * 删除非对称密钥 */ - (void)deleteAsymmetricKeys { OSStatus sanityCheck = noErr; NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init]; NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init]; // 设置公钥查询字典 [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; [queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag]; [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; // 设置私钥查询字典 [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag]; [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; // 删除私钥 sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey); NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除私钥错误,OSStatus == %d", sanityCheck); // 删除公钥 sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey); NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除公钥错误,OSStatus == %d", sanityCheck); if (publicKeyRef) CFRelease(publicKeyRef); if (privateKeyRef) CFRelease(privateKeyRef); } /** * 获得私钥引用 */ - (SecKeyRef)getPrivateKeyRef { OSStatus sanityCheck = noErr; SecKeyRef privateKeyReference = NULL; if (privateKeyRef == NULL) { NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; // 设置私钥查询字典 [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag]; [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef]; // 获得密钥 sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference); if (sanityCheck != noErr) { privateKeyReference = NULL; } } else { privateKeyReference = privateKeyRef; } return privateKeyReference; } @end
使用
- (void)viewDidLoad { [super viewDidLoad]; //1.加载公钥 [[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]]; //2.加载私钥 [[RSACryptor sharedRSACryptor] loadPrivateKey: [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"]; } static void my_encrypt(){ 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]); } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { my_encrypt(); }
七:base64代码、
base64编码(对二进制数据进行base64编码)
这个是ios7之后出现的。
// 对一个二进制数据进行编码,文件会变大! 原有文件的4/3 , 多了1/3 // 不便于查看的二进制数据,用base64进行表示。 //给一个字符 编码 -(NSString *)base64Endcode:(NSString *)str{ NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding]; // 对二进制数据进行base64编码 return [data base64EncodedStringWithOptions:0]; } //给一个编码我对其进行解密 -(NSString *)base64Decode:(NSString *)str{ NSData * data = [[NSData alloc] initWithBase64EncodedString:str options:0]; // 对已经编码的二进制数据进行解码 return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"%@",[self base64Endcode:@"A"]); NSLog(@"解码%@",[self base64Decode:@"QQ=="]); }
注意:
RSA算法就要求加密的明文长度不能超过RSA密钥的长度减去11byte。是自身算法的原因限制的。
说具体点呢,是因为要填充(padding)。RSA实际可加密的明文长度最大其实是1024bits,因为RSA首先就限制了0<明文长度<密钥长度,但问题就来了:
如果小于这个长度怎么办?如果不填充,用户无法确定解密后内容的真实长度,字符串之类的内容问题还不大,以0作为结束符,但对二进制数据就很难理解,因为不确定后面的0是内容还是内容结束符。
只要用到padding,那么就要占用实际的明文长度,于是才有117字节的说法。我们一般使用的padding标准有NoPPadding、OAEPPadding、PKCS1Padding等,其中PKCS#1建议的padding就占用了11个字节。
如果大于这个长度怎么办?很多算法的padding往往是在后边的,但PKCS的padding则是在前面的,此为有意设计,有意的把第一个字节置0以确保明文长度小于密钥长度。
这样,128字节(1024bits)-减去11字节正好是117字节,但对于RSA加密来讲,padding也是参与加密的,所以,依然按照1024bits去理解,但实际的明文只有117字节了。
EB=0x00+BT+PS+0x00+(明文) = 128字节
BT在私钥加密时取0x01,公钥加密取0x02。
PS在私钥加密时取0xFF,公钥加密时取随机值。PS至少要有八个字节长。
Security框架提供的RSA在iOS上使用的一些小结
- 支持的RSA keySize 大小有:512,768,1024,2048位
- 支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
- 签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
- Nopadding填充最大数据块为 下面接口 SecKeyGetBlockSize 大小;
- PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
- OAEP 填充方式最大数据为 SecKeyGetBlockSize 大小减去 42
- RSA加密解密签名,适合小块的数据处理,大量数量需要处理分组逻辑;密码学中推荐使用对称加密进行数据加密,使用RSA来加密对称密钥
- iOS10,以及mac 10.12中新增加了几个接口,以下测试用的是老接口,支持iOS2.0
备注: