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

  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. echo "create certification require file: rsaCertReq.csr"
  6. openssl req -new -key private_key.pem -out rsaCertReq.csr
  7. echo "create certification using x509: rsaCert.crt"
  8. openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt
  9. echo "create public_key.der For IOS"
  10. openssl x509 -outform der -in rsaCert.crt -out public_key.der
  11. echo "create private_key.p12 For IOS. Please remember your password. The password will be used in iOS."
  12. openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
  13. echo "create rsa_public_key.pem For Java"
  14. openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout
  15. echo "create pkcs8_private_key.pem For Java"
  16. openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
  17. echo "finished."

Tips:

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

iOS如何加载使用证书

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

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

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

  1. pod 'Base64nl', '~> 1.2'

加密数据

  1. RSAEncryptor *rsa = [[RSAEncryptor alloc] init];
  2. NSLog(@"encryptor using rsa");
  3. NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
  4. NSLog(@"public key: %@", publicKeyPath);
  5. [rsa loadPublicKeyFromFile:publicKeyPath];
  6. NSString *securityText = @"hello ~";
  7. NSString *encryptedString = [rsa rsaEncryptString:securityText];
  8. 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加密数据.

  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中进行解码:

  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. import static java.lang.String.format;
  17. public class Encryptor {
  18. public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
  19. PrivateKey privateKey = readPrivateKey();
  20. String message = "AFppaFPTbmboMZD55cjCfrVaWUW7+hZkaq16Od+6fP0lwz/yC+Rshb/8cf5BpBlUao2EunchnzeKxzpiPqtCcCITKvk6HcFKZS0sN9wOhlQFYT+I4f/CZITwBVAJaldZ7mkyOiuvM+raXMwrS+7MLKgYXkd5cFPxEsTxpMSa5Nk=";
  21. System.out.println(format("- decrypt rsa encrypted base64 message: %s", message));
  22. // hello ~,  encrypted and encoded with Base64:
  23. byte[] data = encryptedData(message);
  24. String text = decrypt(privateKey, data);
  25. System.out.println(text);
  26. }
  27. private static String decrypt(PrivateKey privateKey, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
  28. Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  29. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  30. byte[] decryptedData = cipher.doFinal(data);
  31. return new String(decryptedData);
  32. }
  33. private static byte[] encryptedData(String base64Text) {
  34. return Base64.getDecoder().decode(base64Text.getBytes(Charset.forName("UTF-8")));
  35. }
  36. private static PrivateKey readPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
  37. byte[] privateKeyData = Files.readAllBytes(
  38. Paths.get("/Users/twer/macspace/ios_workshop/Security/SecurityLogin/tools/pkcs8_private_key.pem"));
  39. byte[] decodedKeyData = Base64.getDecoder()
  40. .decode(new String(privateKeyData)
  41. .replaceAll("-----\\w+ PRIVATE KEY-----", "")
  42. .replace("\n", "")
  43. .getBytes());
  44. return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decodedKeyData));
  45. }
  46. }

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

总结

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

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

参考资料

iOS中使用RSA对数据进行加密解密的更多相关文章

  1. IOS开发数据存储篇—IOS中的几种数据存储方式

    IOS开发数据存储篇—IOS中的几种数据存储方式 发表于2016/4/5 21:02:09  421人阅读 分类: 数据存储 在项目开发当中,我们经常会对一些数据进行本地缓存处理.离线缓存的数据一般都 ...

  2. 使用 GPG 对数据进行加密解密签名

    一:使用 GPG 对数据进行加密解密签名 基本的工具使用 1. GPG 是GNUPG 免费开源的gpg加密工具,和同pgp兼容,pgp收费. 2. 在mac上使用https://gpgtools.or ...

  3. NET实现RSA AES DES 字符串 加密解密以及SHA1 MD5加密

    本文列举了    数据加密算法(Data Encryption Algorithm,DEA) 密码学中的高级加密标准(Advanced EncryptionStandard,AES)RSA公钥加密算法 ...

  4. C# RSA 无 长度限制 加密解密 示例

    RSA 是一种非对称加密算法.由于算法特性,加密和解密过程用不同密钥,即公钥和私钥,而被广泛应用于数字证书的安全管理. 在具体应用中,公钥用加密而私钥用于解密,或 私钥用于数字签名而公钥用于签名验证. ...

  5. 与众不同 windows phone (28) - Feature(特性)之手机方向, 本地化, 应用程序的试用体验, 系统主题资源, 本地数据的加密解密

    原文:与众不同 windows phone (28) - Feature(特性)之手机方向, 本地化, 应用程序的试用体验, 系统主题资源, 本地数据的加密解密 [索引页][源码下载] 与众不同 wi ...

  6. asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密。

    原文:asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密. GitHub demo https://github.com/zhanglilong23/Asp.NetCore. ...

  7. iOS开发之 AES+Base64数据混合加密与解密

    2016-04-08 09:03 编辑: liubinqww 分类:iOS开发 来源:liubinqww 投稿 4 889     "APP的数据安全已经牵动着我们开发者的心,简单的MD5/ ...

  8. iOS中GET 和 POST 数据请求

    iOS中GET 和 POST 网络数据请求 同步请求和异步请求的差别: 1.同步请求,有主线程完成网路请求任务,在数据没有请求之前,用户的所有的交互事件应用都无法处理,会造成一种卡顿现象,影响用户体验 ...

  9. RSA密钥生成、加密解密、签名验签

    RSA 非对称加密公钥加密,私钥解密 私钥签名,公钥验签 下面是生成随机密钥对: //随机生成密钥对 KeyPairGenerator keyPairGen = null; try { keyPair ...

随机推荐

  1. cl_gui_cfw=>dispatch

    将已经触发的EVENT发送给他们各自的EVENT HANDLER,以便让这些事件得到响应. 根据返回值可以判断是否发送成功. CALL METHOD cl_gui_cfw=>dispatch   ...

  2. Dynamics AX for Retail POS Development blogs

    Dynamics AX for Retail POS Development Dynamics AX for Retail POS Development - Code Samples AX for ...

  3. CRM 2013 Reporting Extensions for SSRS 安装及问题解决

    说明一下 Reporting Extensions for SSRS 安装过程. 安装目录在安装目录下 SrsDataConnector 下.如果是CRM 2013安装中运行,可以跳过此步. 此外在说 ...

  4. 与成都的幸福行动家交流GTD

    今年第四次来成都了,通过<小强升职记>的作者邹鑫的撮合,与成都的幸福行动家何平取得了联系,2014年12月6日下午给几个小伙伴们分享了GTD3年来的一点体会.有几位刚接触GTD的朋友,也有 ...

  5. SQL - 生成指定范围内的随机数

    今天按照公司需求,需要做一个sql作业来对数据库定时触发,其中有个难点,就是在sql中需要在1-n中随机出来一个结果. google了半天,找到一个比较好的方式. 写下这个sql: DECLARE @ ...

  6. 这些git技能够你用一年了

    用git有一年了,下面是我这一年来的git使用总结,覆盖了日常使用中绝大多数的场景.嗯,至少是够用一年了,整理出来分享给大家,不明白的地方可以回复交流. git设置关闭自动换行 git config ...

  7. [Weblogic]startWebLogic.cmd配置JAVA_OPTIONS无效的解决思路与方案

    事情背景: 由于某个项目原因,需要在2个域的startWebLogic.cmd文件加入如下设置 set JAVA_OPTIONS=%JAVA_OPTIONS% -javaagent:"pat ...

  8. 关于Redis中的Replication

    一.简介 Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份.具有以下特点: 异步复制 可以配置一主多从 可以配置从服务器可以级联从服务器,既 M- ...

  9. 关于String对象的比较

    1.String对象的比较 String 是一个常量,从String类中的代码可以看出.String类内部是通过char数组来存储字符串,这个char数组是被声明成final的. // Java中只要 ...

  10. 烂泥:ubuntu中使用virt-manager图形化新建虚拟机

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 上一篇文章介绍了,如何在ubuntu下安装KVM的虚拟机管理器virt-manager,这篇文章我们来介绍,如何在图形界面下使用virt-manager ...