背景
RSA這種非對稱加密被廣泛的運用于網絡數據的傳輸,但其在iOS上很難直接實現,BBRSACryptor框架通過移植openssl實現了iOS端的RSA,本文將介紹如何使用BBRSACryptor生成證書,加載公鑰,以及后端如何用php讀取證書,加載私鑰。
iOS加密
新建工程并集成BBRSACryptor
這個框架自帶的demo將工程文件與框架放在了同一目錄,因此在配置Header Search Paths時沒有包含工程文件夾,一定注意,下面新建的工程將框架放在了工程文件夾內,因此頭文件尋找路徑需要包含上工程目錄。詳細步驟如下。
1. 新建一個iOS工程,將BBRSACryptor、GTMBase64、OpenSSL三個文件夾拖入工程,目錄結構如下。
2.在Build Settings中配置Header Search Pathes。
注意最前面的文件夾名稱要和自己的工程名相同
3.打開BBRSACryptor.m文件,修改存儲證書的目錄和文件路徑,默認的是隱藏目錄(前加點),為了方便查看與復制證書,建議將路徑前面的點去掉,例如:
#define OpenSSLRSAKeyDir [DocumentsDir stringByAppendingPathComponent:@"openssl_rsa"]
#define OpenSSLRSAPublicKeyFile [OpenSSLRSAKeyDir stringByAppendingPathComponent:@"publicKey.pem"]
#define OpenSSLRSAPrivateKeyFile [OpenSSLRSAKeyDir stringByAppendingPathComponent:@"privateKey.pem"]
4.打開ViewController.m,導入BBRSACryptor.h和GTMBase64.h,使用下面的代碼生成證書。
BBRSACryptor *rsaCryptor = [[BBRSACryptor alloc] init];
[rsaCryptor generateRSAKeyPairWithKeySize:1024];
運行后,在控制臺會打印出證書路徑,進入路徑后,可以看到公鑰和私鑰證書。
5.使用TextEdit打開公鑰證書,將—–BEGIN PUBLIC KEY—–和—–END PUBLIC KEY—–之間的部分復制,然后在工程中新建一個宏,來保存這個公鑰,以便后續讀取。
#define PublicKey \
@"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjYyZoASYgT+MIc/5YkSJngRbNYEQEI3UF7RVijF0STcMs93pH0qhjLJIQnsvUn2ghEVM4X+S+tQ0XhS+7tmL1UMEFgDgYwG/xr/ZjUozgQyvqeUejA08pbun0E0/Yx9WuBQfCpCc5vNka/ENDZEy/2PbEO5KD3hgsnH1JyNqNnwIDAQAB"
客戶端僅保存公鑰即可,私鑰放在服務器上。使用php可以直接讀取證書。
6.在客戶端加載公鑰與進行加密
前面已經創建了宏,以后通過宏即可加載公鑰。如下:
BBRSACryptor *rsaCryptor = [[BBRSACryptor alloc] init];
// PublicKey是從公鑰證書中復制的內容創建的宏,見上文。
[rsaCryptor importRSAPublicKeyBase64:PublicKey];
NSData *data = [rsaCryptor encryptWithPublicKeyUsingPadding:RSA_PADDING_TYPE_PKCS1 plainData:[@"客戶端加密的內容" dataUsingEncoding:NSUTF8StringEncoding]];
NSString *baseStr = [GTMBase64 stringByEncodingData:data];
NSLog(@"%@",baseStr);
先加載公鑰,然后把要加密的內容轉換成NSData,加密后的內容先進行base64編碼后再傳輸。為了驗證能夠解密,最后對base64編碼的加密內容進行了打印,將這個內容先復制到剪貼板,后面貼在php中進行解密。
php解密
為了方便,將按照上文方法生成的私鑰證書復制到服務器的某個目錄,并在這個目錄下創建一個php文件,并添加如下代碼:
<?phpheader("Content-type:text/html; charset=utf-8");/*** 密鑰文件的路徑*/$privateKeyFilePath = 'privateKey.pem';/*** 公鑰文件的路徑*/$publicKeyFilePath = 'publicKey.pem';extension_loaded('openssl') or die('php需要openssl擴展支持');(file_exists($privateKeyFilePath) && file_exists($publicKeyFilePath))or die('密鑰或者公鑰的文件路徑不正確');/*** 生成Resource類型的密鑰,如果密鑰文件內容被破壞,openssl_pkey_get_private函數返回false*/$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyFilePath));/*** 生成Resource類型的公鑰,如果公鑰文件內容被破壞,openssl_pkey_get_public函數返回false*/$publicKey = openssl_pkey_get_public(file_get_contents($publicKeyFilePath));($privateKey && $publicKey) or die('密鑰或者公鑰不可用');// 這段內容來自上面iOS端打印的加密內容的base64編碼$encryptData = 'J0oTqBCNbsJauVwRz+380y519sSa7ficUO1NvRKiMGKUGJF0pomOu20fHqC77NmsKle9/L4DyYNr3xDgDa4SpO0in39rA9EYXzmx3rlyI1c8iPjAkQ6XpwZk7BsThiCFB/6QmkTW5pMIo4b0axRv/4lq1Rqx/YtuIsGkXQTNntI=';$ee = base64_decode($encryptData);$decryptData ='';if (openssl_private_decrypt($ee, $decryptData, $privateKey)) {echo '解密成功,解密后數據為:', $decryptData, PHP_EOL;} else {die('解密成功');}
?>
訪問這個腳本,如果前面做的沒有問題,會得到解密的結果:
php加密
使用私鑰加密后,可以在客戶端利用公鑰解密。使用下面的代碼進行加密。
<?phpheader("Content-type:text/html; charset=utf-8");/*** 密鑰文件的路徑*/$privateKeyFilePath = 'privateKey.pem';/*** 公鑰文件的路徑*/$publicKeyFilePath = 'publicKey.pem';extension_loaded('openssl') or die('php需要openssl擴展支持');(file_exists($privateKeyFilePath) && file_exists($publicKeyFilePath))or die('密鑰或者公鑰的文件路徑不正確');/*** 生成Resource類型的密鑰,如果密鑰文件內容被破壞,openssl_pkey_get_private函數返回false*/$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyFilePath));/*** 生成Resource類型的公鑰,如果公鑰文件內容被破壞,openssl_pkey_get_public函數返回false*/$publicKey = openssl_pkey_get_public(file_get_contents($publicKeyFilePath));($privateKey && $publicKey) or die('密鑰或者公鑰不可用');
$originalData = '服務器加密的內容';/*** 加密以后的數據,用于在網路上傳輸*/$encryptData = '';echo '原數據為:', $originalData, PHP_EOL;///用私鑰加密if (openssl_private_encrypt($originalData, $encryptData, $privateKey)) {echo '加密成功,加密后數據(base64_encode后)為:', base64_encode($encryptData), PHP_EOL;} else { die('加密失敗'); }
?>
訪問腳本后會打印出加密的base64編碼,將這個編碼復制到客戶端進行解密,來驗證可用性。
iOS解密
要在iOS端解密,和加密類似,先加載公鑰,然后把base64編碼的加密內容解碼,解密后轉為NSString即可。
BBRSACryptor *rsaCryptor = [[BBRSACryptor alloc] init];
[rsaCryptor importRSAPublicKeyBase64:PublicKey];
NSData *enCryptorDataBase64 = [@"aWdbPQHiQzU5CUOAIGQT3OD/MPqcqoXHXDFtYQPVRo9/Mb1S/aVcKQVHDjBpLgfzw+0mWxgHN6SuOfH8z9WobgQrTZh+pxhau3DnfukLmENGPWVMqquWMxTkEU7yCkx/RI7XEwv3jk9d4UgFOv35eqNUgYyWDq2gGatEpfnUg6U=" dataUsingEncoding:NSUTF8StringEncoding];
NSData *deCryptorData = [rsaCryptor decryptWithPublicKeyUsingPadding:RSA_PADDING_TYPE_PKCS1 cipherData:[GTMBase64 decodeData:enCryptorDataBase64]];
NSLog(@"%@",[[NSString alloc] initWithData:deCryptorData encoding:NSUTF8StringEncoding]);
不出意外的話,控制臺將會打印出解密后的內容。