zoukankan      html  css  js  c++  java
  • iOS中使用RSA对数据进行加密解密

    RSA算法是一种非对称加密算法,常被用于加密数据传输.如果配合上数字摘要算法, 也可以用于文件签名.

    本文将讨论如何在iOS中使用RSA传输加密数据.

    本文环境

    • mac os 
    • openssl-1.0.1j, openssl需要使用1.x版本, 推荐使用[homebrew](http://brew.sh/)安装.
    • Java 8

    RSA基本原理

    RSA使用"秘匙对"对数据进行加密解密.在加密解密数据前,需要先生成公钥(public key)和私钥(private key).

    • 公钥(public key): 用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端.
    • 私钥(private key): 用于解密数据. 必须保密, 私钥泄露会造成安全问题.

    iOS中的Security.framework提供了对RSA算法的支持.这种方式需要对密匙对进行处理, 根据public key生成证书, 通过private key生成p12格式的密匙.

    除了Secruty.framework, 也可以将openssl库编译到iOS工程中, 这可以提供更灵活的使用方式.

    本文使用Security.framework的方式处理RSA.

     

    使用openssl生成密匙对

    Github Gist: https://gist.github.com/lvjian700/635368d6f1e421447680

     

     

    Shell代码  

    1. #!/usr/bin/env bash  
    2. echo "Generating RSA key pair ..."  
    3. echo "1024 RSA key: private_key.pem"  
    4. openssl genrsa -out private_key.pem 1024  
    5.   
    6. echo "create certification require file: rsaCertReq.csr"  
    7. openssl req -new -key private_key.pem -out rsaCertReq.csr  
    8.   
    9. echo "create certification using x509: rsaCert.crt"  
    10. openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt  
    11.   
    12. echo "create public_key.der For IOS"  
    13. openssl x509 -outform der -in rsaCert.crt -out public_key.der  
    14.   
    15. echo "create private_key.p12 For IOS. Please remember your password. The password will be used in iOS."  
    16. openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt  
    17.   
    18. echo "create rsa_public_key.pem For Java"  
    19. openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout  
    20. echo "create pkcs8_private_key.pem For Java"  
    21. openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt  
    22.   
    23. echo "finished."  

     

     

     

    Tips:

    • 在创建证书的时候, terminal会提示输入证书信息. 根据wizard输入对应信息就OK. 
    • 在创建p12密匙时, 会提示输入密码, 此时的密码必须记住, 之后会用到.
    • 如果上面指令有问题,请参考最新的openssl官方文档, 以官方的为准. 之前在网上搜索指令, 被坑了一圈之后, 还是会到啃官方文档上. 每条指令文档在最后都会有几个sample,参考sample即可.

    iOS如何加载使用证书

    将下面代码添加到项目中:

    https://gist.github.com/lvjian700/204c23226fdffd6a505d

     

    代码依赖Base64编码库, 如果使用cocoapods, 可以讲下面依赖添加到Podfile:

     

    Ruby代码  

    1. pod 'Base64nl''~> 1.2'  

     

     

    加密数据

     

    Cpp代码  

    1. RSAEncryptor *rsa = [[RSAEncryptor alloc] init];  
    2.   
    3.     NSLog(@"encryptor using rsa");  
    4.     NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];  
    5.     NSLog(@"public key: %@", publicKeyPath);  
    6.     [rsa loadPublicKeyFromFile:publicKeyPath];  
    7.   
    8.     NSString *securityText = @"hello ~";  
    9.     NSString *encryptedString = [rsa rsaEncryptString:securityText];  
    10.     NSLog(@"encrypted data: %@", encryptedString);  

     

     

    __[rsa rsaEncryptString:securityText]__会返回decrypted base64编码的字符串:

    console out 写道

    encrypted data: I1Mnu33cU7QcgaC9uo2bxV0vyfJSqAwyC3DZ+p8jm0G2EmcClarrR5R2xLDdXqvtKj+UJbES7TT+AgkK1NDoQvOJBY+jkmrpAchmRbV2jvi3cEZYQG955jrdSAu21NzQe8xWtEc3YzP+TACPdP4B3Cyy0u8N2RcSFWyxu0YKPXE=

     

    解密数据

    在iOS下解码需要先加载private key, 之后在对数据解码. 解码的时候先进行Base64 decode, 之后在用private key decrypt加密数据.

     

     

    Cpp代码  

    1. NSLog(@"decryptor using rsa");  
    2.     [rsa loadPrivateKeyFromFile:[[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"] password:@"123456"];  
    3.     NSString *decryptedString = [rsa rsaDecryptString:encryptedString];  
    4.     NSLog(@"decrypted data: %@", decryptedString);  

     

     

    之后会输出解密后的数据:

    console 写道

    decryptor using rsa

    decrypted data: hello ~

     

    在服务器端解码数据(Java)

     

    在Java中解码需要使用下述指令生成的pkcs8 private key:

     

    gen shell 写道

    openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

     

    具体解码步骤:

     

    1. 加载pkcs8 private key:
      1. 读取private key文件
      2. 去掉private key头尾的"-----BEGIN PRIVATE KEY-----"和"-----BEGIN PRIVATE KEY-----"
      3. 删除private key中的换行
      4. 对处理后的数据进行Base64解码
      5. 使用解码后的数据生成private key.
    2. 解密数据:
      1. 对数据进行Base64解码
      2. 使用RSA decrypt数据.

    这里我将iOS中"hello ~"加密的数据在Java中进行解码:

     

     

    Java代码  

    1. import javax.crypto.BadPaddingException;  
    2. import javax.crypto.Cipher;  
    3. import javax.crypto.IllegalBlockSizeException;  
    4. import javax.crypto.NoSuchPaddingException;  
    5. import java.io.IOException;  
    6. import java.nio.charset.Charset;  
    7. import java.nio.file.Files;  
    8. import java.nio.file.Paths;  
    9. import java.security.InvalidKeyException;  
    10. import java.security.KeyFactory;  
    11. import java.security.NoSuchAlgorithmException;  
    12. import java.security.PrivateKey;  
    13. import java.security.spec.InvalidKeySpecException;  
    14. import java.security.spec.PKCS8EncodedKeySpec;  
    15. import java.util.Base64;  
    16.   
    17. import static java.lang.String.format;  
    18.   
    19. public class Encryptor {  
    20.   
    21.     public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {  
    22.         PrivateKey privateKey = readPrivateKey();  
    23.   
    24.         String message = "AFppaFPTbmboMZD55cjCfrVaWUW7+hZkaq16Od+6fP0lwz/yC+Rshb/8cf5BpBlUao2EunchnzeKxzpiPqtCcCITKvk6HcFKZS0sN9wOhlQFYT+I4f/CZITwBVAJaldZ7mkyOiuvM+raXMwrS+7MLKgYXkd5cFPxEsTxpMSa5Nk=";  
    25.         System.out.println(format("- decrypt rsa encrypted base64 message: %s", message));  
    26.         // hello ~,  encrypted and encoded with Base64:  
    27.         byte[] data = encryptedData(message);  
    28.         String text = decrypt(privateKey, data);  
    29.         System.out.println(text);  
    30.     }  
    31.   
    32.     private static String decrypt(PrivateKey privateKey, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
    33.         Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
    34.         cipher.init(Cipher.DECRYPT_MODE, privateKey);  
    35.         byte[] decryptedData = cipher.doFinal(data);  
    36.   
    37.         return new String(decryptedData);  
    38.     }  
    39.   
    40.     private static byte[] encryptedData(String base64Text) {  
    41.         return Base64.getDecoder().decode(base64Text.getBytes(Charset.forName("UTF-8")));  
    42.     }  
    43.   
    44.     private static PrivateKey readPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {  
    45.         byte[] privateKeyData = Files.readAllBytes(  
    46.                 Paths.get("/Users/twer/macspace/ios_workshop/Security/SecurityLogin/tools/pkcs8_private_key.pem"));  
    47.   
    48.         byte[] decodedKeyData = Base64.getDecoder()  
    49.                 .decode(new String(privateKeyData)  
    50.                         .replaceAll("-----\w+ PRIVATE KEY-----""")  
    51.                         .replace(" """)  
    52.                         .getBytes());  
    53.   
    54.         return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decodedKeyData));  
    55.     }  
    56. }  

     

     

     

    直行成功后控制台会输出"hello ~".

     

    总结

     

    这种加密传输方式会被用在网银类App中.虽然网银会采用全站https方案, 但是在安全登录这块会使用另一个证书对登录信息加密, 这样可以双层确保数据安全.

    基于RSA加密解密算法, 还可以将其运用在数字签名场景.以后有空在聊如何使用RSA算法实现对文件的数字签名.

     

    参考资料

  • 相关阅读:
    MongoDB的索引(六)
    DMZ原理与应用
    MongoDB的增、删、改、查操作(五)
    一分钟了解mongodb(转)
    mongodb-java-driver基本用法
    Mongodb相对于关系型数据库的优缺点(转)
    什么场景应该用 MongoDB(转)
    MongoDB使用场景和局限 (转)
    matlab7与win7不兼容
    sublime的使用
  • 原文地址:https://www.cnblogs.com/mingjieLove00/p/5485673.html
Copyright © 2011-2022 走看看