看到這篇文章的同學可幸福了糕韧,當時在做RSA加密與簽名的時候網上的資料簡直不要太老酱吝,做完后實在是忍受不下去了禽车,這篇文章我會詳細講解iOS如何實現RSA加密與簽名艇搀,并且與Java完全同步臀蛛,這是我的第二篇博客亲桦,若有什么不足之處還請大家指教。
基礎知識
<p>
什么是RSA浊仆?
答:RSA是一種非對稱加密算法客峭,常用來對傳輸數據進行加密,配合上數字摘要算法抡柿,也可以進行文字簽名舔琅。RSA加密中padding?
答:padding即填充方式洲劣,由于RSA加密算法中要加密的明文是要比模數小的备蚓,padding就是通過一些填充方式來限制明文的長度。后面會詳細介紹padding的幾種模式以及分段加密闪檬。加密和加簽有什么區(qū)別星著?
答:加密:公鑰放在客戶端,并使用公鑰對數據進行加密粗悯,服務端拿到數據后用私鑰進行解密虚循;
加簽:私鑰放在客戶端,并使用私鑰對數據進行加簽样傍,服務端拿到數據后用公鑰進行驗簽横缔。
前者完全為了加密;后者主要是為了防惡意攻擊衫哥,防止別人模擬我們的客戶端對我們的服務器進行攻擊茎刚,導致服務器癱瘓。
基本原理<p>
RSA使用“密鑰對”對數據進行加密解密撤逢,在加密解密前需要先生存公鑰(Public Key)和私鑰(Private Key)膛锭。
公鑰(Public key): 用于加密數據. 用于公開, 一般存放在數據提供方, 例如iOS客戶端粮坞。
私鑰(Private key): 用于解密數據. 必須保密, 私鑰泄露會造成安全問題。
iOS中的Security.framework提供了對RSA算法的支持初狰,這種方式需要對密匙對進行處理, 根據public key生成證書, 通過private key生成p12格式的密匙莫杈。想想jave直接用字符串進行加密解密簡單多了。(⊙o⊙)…
實戰(zhàn)
<p>
證書生成
RSA加密這塊公鑰奢入、私鑰必不可少的筝闹。Apple是不支持直接使用字符串進行加密解密的,推薦使用p12文件腥光。這邊教大家去生成在加密中使用到的所有文件关顷,并提供給Java使用,想當年這個公鑰私鑰搞了半天了武福。 %>_<%
- 生成模長為1024bit的私鑰
openssl genrsa -out private_key.pem 1024
- 生成certification require file
openssl req -new -key private_key.pem -out rsaCertReq.csr - 生成certification 并指定過期時間
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt - 生成公鑰供iOS使用
openssl x509 -outform der -in rsaCert.crt -out public_key.der - 生成私鑰供iOS使用 這邊會讓你輸入密碼议双,后期用到在生成secKeyRef的時候會用到這個密碼
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt - 生成pem結尾的公鑰供Java使用
openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout - 生成pem結尾的私鑰供Java使用openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
以上所有的步驟都是在終端下完成的哦 (__)**
生成公鑰和私鑰的secKeyRef<p>
//根據你的p12文件生成私鑰對應的SecKeyRef 這邊返回若是nil 請檢查你p12文件的生成步驟
- (SecKeyRef)getPrivateKeyRefrenceFromData:(NSData*)p12Data password:(NSString*)password {
SecKeyRef privateKeyRef = NULL;
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
[options setObject: password forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data, (__bridge CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
CFRelease(items);
return privateKeyRef;
}
-
//根據你的der文件公鑰對應的SecKeyRef
- (SecKeyRef)getPublicKeyRefrenceFromeData: (NSData*)derData {
SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
SecTrustResultType trustResult;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult);
}
SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);
CFRelease(myCertificate);
CFRelease(myPolicy);
CFRelease(myTrust);return securityKey;
}
加密與解密 <p>
- (NSData*)rsaEncryptData:(NSData*)data {
SecKeyRef key = [self getPublicKey];
size_t cipherBufferSize = SecKeyGetBlockSize(key);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
size_t blockSize = cipherBufferSize - 11;
size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
NSMutableData *encryptedData = [[NSMutableData alloc] init];
for (int i=0; i<blockCount; i++) {
unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes], [buffer length], cipherBuffer, &cipherBufferSize);
if (status != noErr) {
return nil;
}
NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}
if (cipherBuffer){
free(cipherBuffer);
}
return encryptedData;
}
-
- (NSData)rsaDecryptData:(NSData)data {
SecKeyRef key = [self getPrivatKey];
size_t cipherBufferSize = SecKeyGetBlockSize(key);
size_t blockSize = cipherBufferSize;
size_t blockCount = (size_t)ceil([data length] / (double)blockSize);NSMutableData *decryptedData = [[NSMutableData alloc] init];
for (int i = 0; i < blockCount; i++) {
unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];size_t cipherLen = [buffer length]; void *cipher = malloc(cipherLen); [buffer getBytes:cipher length:cipherLen]; size_t plainLen = SecKeyGetBlockSize(key); void *plain = malloc(plainLen); OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen); if (status != noErr) { return nil; } NSData *decryptedBytes = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen]; [decryptedData appendData:decryptedBytes];
}
return decryptedData;
}
<p> - (NSData)rsaDecryptData:(NSData)data {
RSA加密中的Padding<p>
RSA_PKCS1_PADDING 填充模式,最常用的模式
要求: 輸入:必須 比 RSA 鑰模長(modulus) 短至少11個字節(jié), 也就是 RSA_size(rsa) – 11 如果輸入的明文過長捉片,必須切割聋伦,然后填充。
輸出:和modulus一樣長
根據這個要求界睁,對于1024bit的密鑰觉增,block length = 1024/8 – 11 = 117 字節(jié)RSA_PKCS1_OAEP_PADDING
輸入:RSA_size(rsa) – 41
輸出:和modulus一樣長RSA_NO_PADDING 不填充
輸入:可以和RSA鑰模長一樣長,如果輸入的明文過長翻斟,必須切割逾礁, 然后填充
輸出:和modulus一樣長
簽名與驗證<p>
//對數據進行sha256簽名
- (NSData *)rsaSHA256SignData:(NSData *)plainData {
SecKeyRef key = [self getPrivatKey];
size_t signedHashBytesSize = SecKeyGetBlockSize(key);
uint8_t* signedHashBytes = malloc(signedHashBytesSize);
memset(signedHashBytes, 0x0, signedHashBytesSize);
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
uint8_t* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
return nil;
}
SecKeyRawSign(key,
kSecPaddingPKCS1SHA256,
hashBytes,
hashBytesSize,
signedHashBytes,
&signedHashBytesSize);
NSData* signedHash = [NSData dataWithBytes:signedHashBytes
length:(NSUInteger)signedHashBytesSize];
if (hashBytes)
free(hashBytes);
if (signedHashBytes)
free(signedHashBytes);
return signedHash;
}
//這邊對簽名的數據進行驗證 驗簽成功,則返回YES - (BOOL)rsaSHA256VerifyData:(NSData *)plainData withSignature:(NSData *)signature { SecKeyRef key = [self getPublicKey]; size_t signedHashBytesSize = SecKeyGetBlockSize(key); const void* signedHashBytes = [signature bytes]; size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH; uint8_t* hashBytes = malloc(hashBytesSize); if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) { return NO; } OSStatus status = SecKeyRawVerify(key, kSecPaddingPKCS1SHA256, hashBytes, hashBytesSize, signedHashBytes, signedHashBytesSize); return status == errSecSuccess; }
文章到此就結束了访惜,希望這篇文章對大家有所幫助嘹履。想看demo的請點擊:XYRSACryptor