本文主要介绍RSA的代码演示过程
RSA代码演示
前提:准备好公钥、私钥,需要在终端生成(属于自己签名)
证书申请步骤
- 1、申请
CSR
文件:keychain -> 证书助理 -> 从证书颁发机构请求证书 - 2、生成
CSR请求文件
(证书颁发机构信息 + 公钥)
- 命令:
openssl req -new -key private.pem -out rsacert.csr
- 3、生成
CRT
证书(自己签名,没有认证的)
- 命令:
openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
- 4、生成der文件
- 命令:
openssl x509 -outform der -in rsacert.crt -out rsacert.der
5、获取p12
文件
- 命令:
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
- 注:代码中使用
der
格式
base64编码
base64编码由0-9、a-z、A-Z + /
(64个字符 )加上 =
(表示补零) 来组成的文本
终端命令
- vi message.txt
- base64编码:base64 message.txt -o abc.txt
- base64解码:base abc.txt -o 123.txt -D
代码演示
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"en:%@", [self base64Encode:@"A"]); NSLog(@"de:%@", [self base64Decode:@"QQ=="]); } //编码 - (NSString *)base64Encode:(NSString *)string{ NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; return [data base64EncodedStringWithOptions: 0]; } //解码 - (NSString *)base64Decode:(NSString *)string{ NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } <!--打印结果--> en:QQ== de:A
base64说明
- 1、base64只适用于表示
二进制文件
- 2、base64编码后,文件数量变多,不适合对大型数据进行编码
- 3、bse64和数据是
一一对应
的
代码演示
前提:通过证书申请步骤,准备好p12和der文件
- 1、创建RSA加解密类:RSACryptor
<!--RSACryptor.h--> #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 <!--RSACryptor.m--> #import "RSACryptor.h" // 填充模式 /* - kSecPaddingNone 不填充 - kSecPaddingPKCS1 填充 */ #define kTypeOfWrapPadding kSecPaddingPKCS1 // 公钥/私钥标签 #define kPublicKeyTag "com.cjl.EncryptDemo.publickey" #define kPrivateKeyTag "com.cjl.EncryptDemo.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; 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, @"信任评估失败"); // 提取私钥 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
- 2、通过代码加载公钥(der文件)和私钥(p12文件)
- (void)testRSA{ //1、加载公钥 [[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]]; //2、加载私钥 [[RSACryptor sharedRSACryptor] loadPrivateKey:[[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"]; }
- 3、使用RSA进行加解密
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //加密 NSData *result = [[RSACryptor sharedRSACryptor] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]]; NSString *base64 = [result base64EncodedStringWithOptions:0]; NSLog(@"en - %@", base64); //解密 NSData *jiemi = [[RSACryptor sharedRSACryptor] decryptData:result]; NSLog(@"de - %@", [[NSString alloc] initWithData:jiemi encoding:NSUTF8StringEncoding]); } <!--打印结果--> en - L+1uUQ9eSzZmVJuEXMZ7Z8Wr241ze/6XbKMoBTLDdCvlf2bLcJPDJor5RVvn00rPg65NLwd3AyZDy+4/3t41bAJtHo2+MjmAHJ32rmTTx/HH5B3WOghOGqhLZS1hLFt62tic8betewTgzJg9IvMbtSvXDl4XdgLXM8ZWFdosneg= de - hello
从结果中可以发现,每次RSA加密结果不一样,原因是因为RSA有个填充模式,导致每次结果不一样
kSecPaddingNone
不填充,密文每次不变kSecPaddingPKCS1
填充,密文随机变化
总结
- base64编码说明:
- 1、base64只适用于表示
二进制文件
- 2、base64编码后,文件数量变多,不适合对大型数据进行编码
- 3、bse64和数据是
一一对应
的
- RSA代码加解密是通过
Security
框架
- 加密:使用
SecKeyEncrypt
函数 - 解密:使用
SecKeyDecrypt
函数 - 加密有两种填充模式:
kSecPaddingNone
不填充,密文每次不变kSecPaddingPKCS1
填充,密文每次随机变化