本文主要介紹RSA的代碼演示過程
RSA代碼演示
前提:準備好公鑰、私鑰,需要在終端生成(屬于自己簽名)
證書申請步驟
1、申請
CSR
文件:keychain -> 證書助理 -> 從證書頒發(fā)機構請求證書-
2、生成
CSR請求文件
(證書頒發(fā)機構信息 + 公鑰)- 命令:
openssl req -new -key private.pem -out rsacert.csr
證書申請步驟-02
- 命令:
-
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
證書申請步驟-04
- 命令:
-
5、獲取
p12
文件- 命令:
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
證書申請步驟-05
注:代碼中使用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編碼后,文件數(shù)量變多,不適合對大型數(shù)據(jù)進行編碼
3、bse64和數(shù)據(jù)是
一一對應
的
代碼演示
前提:通過證書申請步驟,準備好p12和der文件
- 1、創(chuàng)建RSA加解密類:RSACryptor
<!--RSACryptor.h-->
#import <Foundation/Foundation.h>
@interface RSACryptor : NSObject
+ (instancetype)sharedRSACryptor;
/**
* 生成密鑰對
*
* @param keySize 密鑰尺寸,可選數(shù)值(512/1024/2048)
*/
- (void)generateKeyPair:(NSUInteger)keySize;
/**
* 加載公鑰
*
* @param publicKeyPath 公鑰路徑
*
@code
# 生成證書
$ openssl genrsa -out ca.key 1024
# 創(chuàng)建證書請求
$ 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;
/**
* 加密數(shù)據(jù)
*
* @param plainData 明文數(shù)據(jù)
*
* @return 密文數(shù)據(jù)
*/
- (NSData *)encryptData:(NSData *)plainData;
/**
* 解密數(shù)據(jù)
*
* @param cipherData 密文數(shù)據(jù)
*
* @return 明文數(shù)據(jù)
*/
- (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 - 加密 & 解密數(shù)據(jù)
- (NSData *)encryptData:(NSData *)plainData {
OSStatus sanityCheck = noErr;
size_t cipherBufferSize = 0;
size_t keyBufferSize = 0;
NSAssert(plainData != nil, @"明文數(shù)據(jù)為空");
NSAssert(publicKeyRef != nil, @"公鑰為空");
NSData *cipher = nil;
uint8_t *cipherBuffer = NULL;
// 計算緩沖區(qū)大小
cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
keyBufferSize = [plainData length];
if (kTypeOfWrapPadding == kSecPaddingNone) {
NSAssert(keyBufferSize <= cipherBufferSize, @"加密內(nèi)容太大");
} else {
NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密內(nèi)容太大");
}
// 分配緩沖區(qū)
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);
// 生成密文數(shù)據(jù)
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, @"私鑰不存在");
// 計算緩沖區(qū)大小
cipherBufferSize = SecKeyGetBlockSize(privateKey);
keyBufferSize = [cipherData length];
NSAssert(keyBufferSize <= cipherBufferSize, @"解密內(nèi)容太大");
// 分配緩沖區(qū)
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);
// 生成明文數(shù)據(jù)
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 表示的證書創(chuàng)建一個證書對象
NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
NSAssert(certificateRef != NULL, @"公鑰文件錯誤");
// 返回一個默認 X509 策略的公鑰對象,使用之后需要調用 CFRelease 釋放
SecPolicyRef policyRef = SecPolicyCreateBasicX509();
// 包含信任管理信息的結構體
SecTrustRef trustRef;
// 基于證書和策略創(chuàng)建一個信任管理對象
OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
NSAssert(status == errSecSuccess, @"創(chuàng)建信任管理對象失敗");
// 信任結果
SecTrustResultType trustResult;
// 評估指定證書和策略的信任管理是否有效
status = SecTrustEvaluate(trustRef, &trustResult);
NSAssert(status == errSecSuccess, @"信任評估失敗");
// 評估之后返回公鑰子證書
publicKeyRef = SecTrustCopyPublicKey(trustRef);
NSAssert(publicKeyRef != NULL, @"公鑰創(chuàng)建失敗");
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 格式數(shù)據(jù)中的標示和證書
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, @"私鑰創(chuàng)建失敗");
}
/**
* 刪除非對稱密鑰
*/
- (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
從結果中可以發(fā)現(xiàn),每次RSA加密結果不一樣,原因是因為RSA有個填充模式,導致每次結果不一樣
kSecPaddingNone
不填充,密文每次不變-
kSecPaddingPKCS1
填充,密文隨機變化
RSA加密結果
總結
- base64編碼說明:
1、base64只適用于表示
二進制文件
2、base64編碼后,文件數(shù)量變多,不適合對大型數(shù)據(jù)進行編碼
3、bse64和數(shù)據(jù)是
一一對應
的
- RSA代碼加解密是通過
Security
框架加密:使用
SecKeyEncrypt
函數(shù)解密:使用
SecKeyDecrypt
函數(shù)-
加密有兩種填充模式:
kSecPaddingNone
不填充,密文每次不變kSecPaddingPKCS1
填充,密文每次隨機變化