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算法实现对文件的数字签名.

    参考资料

  • 相关阅读:
    Oracle 安装报错 [INS-06101] IP address of localhost could not be determined 解决方法输入日志标题
    Linux下安装oracle数据库提示DISPLAY not set. Please set the DISPLAY and try again。
    redhat 关机注销命令详解
    VirtualBox的四种网络连接方式
    修改RedHat的系统显示时间
    insufficient memory to configure kdump(没有足够的内存)解决方法(待验证、待解决)
    xen坑随笔 heartbeat dpkg垃圾数据库清除
    tomcat 监控脚本
    负载均衡随笔
    GIT命令介绍
  • 原文地址:https://www.cnblogs.com/On1Key/p/5459355.html
Copyright © 2011-2022 走看看