RSA部分基础
通常我们所说的RSA公钥1024位,2048位是RSA算法的模长度,https的TSL ssl部分也是使用RSA来进行加密握手.
RSA基本原理(略过)
$ openssl req -x509 -out public_key.der -outform der -new -newkey rsa:1024 -keyout private_key.pem -days 3650
- public_key.der is an output based on x509 certificate. Note that in iOS must be .der format but not .pem
- private_key.pem is the private key that you can use it to decrypt
- rsa:1024 有时候是2048.is the key length. The longer the length, the safer it is
- days is the days for effective period for this cert. In this case, is 10-Years
然后,将public_key.der拖到iOS project然后使用RSA.h RSA.m .
参考: http://jslim.net/blog/2013/01/05/rsa-encryption-in-ios-and-decrypt-it-using-php/
RSA加密中的Padding
padding即是填充方式,先说下为什么会有padding,由于RSA的算法原理中,需要被加密的明文c是要比模数小的。padding就是通过一些填充方式保证明文c的位数,且不能使c大于n.因此模的长度也觉得可以加密明文的长度,因此RSA不适合加密大段文本,一般用来加密一个对称加密的密钥,然后再用此对称加密密钥对大段文本加密。一般选择模式是RSA_PKCS1_PADDING
IOS中的RSA
在ios中可以使用
//加密方法
OSStatus SecKeyEncrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *plainText,
size_t plainTextLen,
uint8_t *cipherText,
size_t *cipherTextLen)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
// 解密方法
OSStatus SecKeyDecrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *cipherText,
size_t cipherTextLen,
uint8_t *plainText,
size_t *plainTextLen)
iOS拿到的公钥类型
第一种: pem格式文件 或者 base64的 public key
一个pem格式的公钥文件,其中保存的公钥信息是base64编码的字符串,这个用iOS 系统sercurity.framework的api就不能读取。
后台经常会给一个xxx.pem
的文件. 里面的内容用sublime打开就如上所示.
下面是pem内容示例(ps: 每行64个字符)
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApfNDz78E4qnU23PeE3lQ
DYaW9AtniNMWNn51eMANFtiFj6yvpAo+ejstkC4u0PevoA9YrhPUC97f8xd5Jss2
INyynKH3okVS4at5lxBBdpoqR3LxLSQPRcmh5h19PZj0/B6QO1Lm66qoxjoQUjoQ
4eqryb0zD5vwu+6kCQHH/sHSky8ZYdUH9p5baJOmLUFpqROPWZpliq7qxFTuLLoZ
jHEgbdPad/IX9IouYzrlnhQzUP+WjugnLU6NoeoKUer9YtPZg1WVbg/n98orsb8H
hmJL/RtAqWpOPMvEoYLGyiguFEPgjvYPV2dnSJxkTgb3XqnnujTN1BFsYDWmMC9S
TQIDAQAB
-----END PUBLIC KEY-----
实际, 上述文件中每行结尾都有一个隐藏的换行符\n
,如果把换行符加上,然后写成一个NSString:
NSSring *pemPublicKey = @"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApfNDz78E4qnU23PeE3lQ\nDYaW9AtniNMWNn51eMANFtiFj6yvpAo+ejstkC4u0PevoA9YrhPUC97f8xd5Jss2\nINyynKH3okVS4at5lxBBdpoqR3LxLSQPRcmh5h19PZj0/B6QO1Lm66qoxjoQUjoQ\n4eqryb0zD5vwu+6kCQHH/sHSky8ZYdUH9p5baJOmLUFpqROPWZpliq7qxFTuLLoZ\njHEgbdPad/IX9IouYzrlnhQzUP+WjugnLU6NoeoKUer9YtPZg1WVbg/n98orsb8H\nhmJL/RtAqWpOPMvEoYLGyiguFEPgjvYPV2dnSJxkTgb3XqnnujTN1BFsYDWmMC9S\nTQIDAQAB\n-----END PUBLIC KEY-----";
这种是上面pem格式中间部分, 同时也不包含回车换行, 需要每64个字符添加一个换行符.
// base64编码的字符串
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApfNDz78E4qnU23PeE3lQDYaW9AtniNMWNn51eMANFtiFj6yvpAo+ejstkC4u0PevoA9YrhPUC97f8xd5Jss2INyynKH3okVS4at5lxBBdpoqR3LxLSQPRcmh5h19PZj0/B6QO1Lm66qoxjoQUjoQ4eqryb0zD5vwu+6kCQHH/sHSky8ZYdUH9p5baJOmLUFpqROPWZpliq7qxFTuLLoZjHEgbdPad/IX9IouYzrlnhQzUP+WjugnLU6NoeoKUer9YtPZg1WVbg/n98orsb8HhmJL/RtAqWpOPMvEoYLGyiguFEPgjvYPV2dnSJxkTgb3XqnnujTN1BFsYDWmMC9STQIDAQAB
第二种: public key 的16进制字符串
第二种,后台可能直接给Public Key 的16进制的字符串(Hex String), 或者 2进制字符串, 这种公钥一般称为裸公钥, HJ就是提供的这种中key.
16进制字符串表示的Public key
一共512位16进制,2进制位2048位(ps: 也有256位16进制,2进制1024位的public key).这种裸公钥相当于上面pem格式中的中间部分(除了-BEGIN- -END-两行, 并且中间部分不包含\n
回车换行符), 只不过二进制/十六进制数据通过进行了base64编码成字符串,用sublime可以直接打开.
下面是一个16进制字符串public key, 提供512位16进制的字符串:
E00B364554A35D6705468698DC366115C09F2BA109D1F3A45901EF8872429752A0DAED95F50B31977FCF1989BC105143444D4583A479478F89148F1A35DACBACAC9584BFC528BE1AFF94685177514468F96F707BCBCD4F76DF989E5D7223384593AE81A1F2254D7CDD1CCA7BA01B006C4BAD87C2D270C51BF48B6CCECFA7022CAE93E9B17D31F0DB7A59E68D103BC99A3DDCA411CDDF2BC5558C7A88A884EFD33E52253C6D9E050F54C813A5EF38688C7A547072685399E5759FCFD03FD22B21E58D583C4D46EC5582A78FE4E18C10AF081970EEB14FEEDAA04F315D216EF988CB16F84FBBEE767CD51EDFFB23E83B303AD715B6E7A9D6CC475496F9BA3951A5
这种模式比较好存储, 我们可以通过编写代码将这种格式的数据转化成第一种格式(PEM)格式,
将裸Public Key转化成为需要
- 为裸 public_key 添加完整的头部和尾部
- 头部:30820122300D06092A864886F70D01010105000382010F003082010A0282010100
- 尾部: 0203010001
- 然后执行base64编码,然后每一行64位换行处理,加标准头和尾部就是标准的PEM文件的中间部分。
在Terminal中直接运行下面命令可以得到base64编码的public key
16进制字符串显示 -> 二进制字符串显示 -> base64编码
echo 30820122300D06092A864886F70D01010105000382010F003082010A0282010100E00B364554A35D6705468698DC366115C09F2BA109D1F3A45901EF8872429752A0DAED95F50B31977FCF1989BC105143444D4583A479478F89148F1A35DACBACAC9584BFC528BE1AFF94685177514468F96F707BCBCD4F76DF989E5D7223384593AE81A1F2254D7CDD1CCA7BA01B006C4BAD87C2D270C51BF48B6CCECFA7022CAE93E9B17D31F0DB7A59E68D103BC99A3DDCA411CDDF2BC5558C7A88A884EFD33E52253C6D9E050F54C813A5EF38688C7A547072685399E5759FCFD03FD22B21E58D583C4D46EC5582A78FE4E18C10AF081970EEB14FEEDAA04F315D216EF988CB16F84FBBEE767CD51EDFFB23E83B303AD715B6E7A9D6CC475496F9BA3951A50203010001 | xxd -r -ps | openssl base64
输出的结果是:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4As2RVSjXWcFRoaY3DZh
FcCfK6EJ0fOkWQHviHJCl1Kg2u2V9Qsxl3/PGYm8EFFDRE1Fg6R5R4+JFI8aNdrL
rKyVhL/FKL4a/5RoUXdRRGj5b3B7y81Pdt+Ynl1yIzhFk66BofIlTXzdHMp7oBsA
bEuth8LScMUb9Itszs+nAiyuk+mxfTHw23pZ5o0QO8maPdykEc3fK8VVjHqIqITv
0z5SJTxtngUPVMgTpe84aIx6VHByaFOZ5XWfz9A/0ish5Y1YPE1G7FWCp4/k4YwQ
rwgZcO6xT+7aoE8xXSFu+YjLFvhPu+52fNUe3/sj6DswOtcVtuep1sxHVJb5ujlR
pQIDAQAB
然后手动添加首部和尾部,最终结果就和上面PEM格式内容一样了:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApfNDz78E4qnU23PeE3lQ
DYaW9AtniNMWNn51eMANFtiFj6yvpAo+ejstkC4u0PevoA9YrhPUC97f8xd5Jss2
INyynKH3okVS4at5lxBBdpoqR3LxLSQPRcmh5h19PZj0/B6QO1Lm66qoxjoQUjoQ
4eqryb0zD5vwu+6kCQHH/sHSky8ZYdUH9p5baJOmLUFpqROPWZpliq7qxFTuLLoZ
jHEgbdPad/IX9IouYzrlnhQzUP+WjugnLU6NoeoKUer9YtPZg1WVbg/n98orsb8H
hmJL/RtAqWpOPMvEoYLGyiguFEPgjvYPV2dnSJxkTgb3XqnnujTN1BFsYDWmMC9S
TQIDAQAB
-----END PUBLIC KEY-----
第二种解决方法, 直接转化成证书der格式的数据
例如,随便找一张der证书, 将它转化成的16进制字符串展示:
30820308308201f00209008932a038393835a1300d06092a864886f70d01010505003045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c74643020170d3135303631303037333931355a180f32313135303531373037333931355a3045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c746430820122300d06092a864886f70d01010105000382010f003082010a0282010100
*E00B364554A35D6705468698DC366115C09F2BA109D1F3A45901EF8872429752A0DAED95F50B31977FCF1989BC105143444D4583A479478F89148F1A35DACBACAC9584BFC528BE1AFF94685177514468F96F707BCBCD4F76DF989E5D7223384593AE81A1F2254D7CDD1CCA7BA01B006C4BAD87C2D270C51BF48B6CCECFA7022CAE93E9B17D31F0DB7A59E68D103BC99A3DDCA411CDDF2BC5558C7A88A884EFD33E52253C6D9E050F54C813A5EF38688C7A547072685399E5759FCFD03FD22B21E58D583C4D46EC5582A78FE4E18C10AF081970EEB14FEEDAA04F315D216EF988CB16F84FBBEE767CD51EDFFB23E83B303AD715B6E7A9D6CC475496F9BA3951A5*
0203010001300d06092a864886f70d0101050500038201010045c2e98898344a645889e227c112bbbaf09a66105e5e964a90f298420e4187510b4c13c705177ebf605a09c7df74b2ed51fe117a2ad291ee554ecd38db3c92ec06e9b5c1df27ca519d5c0330986bf7b6447e5e8e87607febef6ace24111a308840543458a52b365ac6ac392bf009df15bbba44155d095ddb711dd9599bb10c5d8a55f8531d09fa5be8db2dcdbcce869ea04a4dbfcd693f1f67689c3b1c259a3ece586008a69cdae17892cdcb6a11d45680b6854721c185937b24295b0fa115b9751020f65d6bfda0f4fb47a8982e80c7c3682daa113ef24bae4a7e82aaa2aa634bab8faa68b28b3d999720dc9c2fc272dc488d2256f0cd1dd8a13a400ab9dd6c
- 找到
30820122300D06092A864886F70D01010105000382010F003082010A0282010100
(这段代表-----BEGIN PUBLIC KEY-----) - 找到尾部'0203010001' (-----END PUBLIC KEY-----)
- 两者之间刚好512位16进制数据,就是证书的2048位 publi key所在的地方. 直接用自己的16进制字符串public key换掉这部分,就伪造了一个后缀是.der的证书
注意: .der证书内还包括很多信息,例如地址.公司,过期时间.等等,一般rsa加密只用中间256字节(2048位)的public key就行了
其他参考内容 - 后台给出的 java 参考
后台给出的使用public key的参考:
String publicKeyStr = "30820122300D06092A864886F70D01010105000382010F003082010A0282010100"
+ "E00B364554A35D6705468698DC366115C09F2BA109D1F3A45901EF8872429752A0DAED95F50B31977FCF1989BC105143444D4583A479478F89148F1A35DACBACAC9584BFC528BE1AFF94685177514468F96F707BCBCD4F76DF989E5D7223384593AE81A1F2254D7CDD1CCA7BA01B006C4BAD87C2D270C51BF48B6CCECFA7022CAE93E9B17D31F0DB7A59E68D103BC99A3DDCA411CDDF2BC5558C7A88A884EFD33E52253C6D9E050F54C813A5EF38688C7A547072685399E5759FCFD03FD22B21E58D583C4D46EC5582A78FE4E18C10AF081970EEB14FEEDAA04F315D216EF988CB16F84FBBEE767CD51EDFFB23E83B303AD715B6E7A9D6CC475496F9BA3951A5"
+ "0203010001";
一般js中给的是裸公钥,即512位16进制(实际为二进制的2048位)
因为NDK中实现加密和解密,需要完整的公钥和私钥,所以需要相应的头部和尾部。
头部:30820122300D06092A864886F70D01010105000382010F003082010A0282010100
尾部: 0203010001
然后执行base64编码,然后每一行64位换行处理,加标准头和尾部就是标准的公钥了。
base64编码的命令: termial中,直接运行下面命令可以得到标准的公钥:
echo 30820122300D06092A864886F70D01010105000382010F003082010A0282010100A5F343CFBF04E2A9D4DB73DE1379500D8696F40B6788D316367E7578C00D16D8858FACAFA40A3E7A3B2D902E2ED0F7AFA00F58AE13D40BDEDFF3177926CB3620DCB29CA1F7A24552E1AB79971041769A2A4772F12D240F45C9A1E61D7D3D98F4FC1E903B52E6EBAAA8C63A10523A10E1EAABC9BD330F9BF0BBEEA40901C7FEC1D2932F1961D507F69E5B6893A62D4169A9138F599A658AAEEAC454EE2CBA198C71206DD3DA77F217F48A2E633AE59E143350FF968EE8272D4E8DA1EA0A51EAFD62D3D98355956E0FE7F7CA2BB1BF0786624BFD1B40A96A4E3CCBC4A182C6CA282E1443E08EF60F576767489C644E06F75EA9E7BA34CDD4116C6035A6302F524D0203010001| xxd -r -ps | openssl base64
其他参考文档:
关于PEM格式的public key 进行加密, 解密可以参考以下开源库:
https://github.com/ideawu/Objective-C-RSA
https://github.com/kuapay/iOS-Certificate--Key--and-Trust-Sample-Project
后面两个项目需要引入openssl库
https://blog.cnbluebox.com/blog/2014/03/19/rsajia-mi/
https://github.com/NianJi/BBRSACryptor