RSA 入门介绍及应用
RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥
关于RSA的密钥
- RSA的密钥主要由2个参数组成,modulus和exponent,经过一定的算法生成,具体可以去wiki了解一下
- 通常我们看到的RSA的密钥大概长成下面这个样子,有个开头和结尾的标识,这种是经过base64之后的值
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKiMx2aFW1L5aceZ6j0AxFaz38aPgadH
AOZyxEHG/7g6ia/pWgDw73JWpUP65mkpVymfbln/GqJJoMxtXDnUGOMCAwEAAQ==
-----END PUBLIC KEY-----
- RSA的密钥都是成对的,一个是Private,一个是Public,用一个加密,就可以用另外一个解密
- Public可以从Private中生成,因为大家的modulus都是一样的,但是Private不能从Public中提取
生成一个RSA的Keypair
- 通过在线工具来生成 http://travistidwell.com/jsencrypt/demo/
- 这个可以简单地生成一个相对通用的base64的字符串key
- 通过命令行的openssl来生成
- 生成私钥:
$ openssl genrsa -out private.pem 1024
- 通过私钥生成公钥:
$ openssl rsa -in private.pem -out public.pem -outform PEM -pubout
- 通过
-text -noout (-modulus)
参数可以查看生成的key的modulus和exponent,默认是16进制的, - 可以通过
echo “ibase=16,xxxxx” | bc
来输出10进制的数字,用来给一些底层第三方RSA的代码使用,例如后面提到那份 - Apple的api
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
OSStatus SecKeyGeneratePair(CFDictionaryRef parameters,
SecKeyRef *publicKey,
SecKeyRef *privateKey)
- 这里生成一个SecKeyRef的数据结构之后,publickey可以很方便的获取到modulus和exponent,然后网上可以有算法转成类似的字符串,privateKey的话这里就比较麻烦了,因此使用原生的api对Verfiry和Decrypt是比较友好的,对于使用第三方生成的key来sign和encrypt都不是很友好,转换太麻烦了。还没有找到方法可以从已有数据生成一个privateKey,只能生成一个publickKey。
使用RSA进行加密和解密
- 命令行 可以快速验证和测试
- 加密:
$ openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl
- 解密:
$ openssl rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt
- Apple的api
- 加密:
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, /* Private key */
SecPadding padding, /* kSecPaddingNone,
kSecPaddingPKCS1,
kSecPaddingOAEP */
const uint8_t *cipherText,
size_t cipherTextLen, /* length of cipherText */
uint8_t *plainText,
size_t *plainTextLen) /* IN/OUT */
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
- 其他第三方的RSA实现 http://rsa.googlecode.com/svn/rsa/trunk/
使用RSA进行签名和验证
-
和加密解密一样,encrypt/decrypt换成sign/verify,粗糙理解的话,verify可以理解为能否解密吧
-
对于Apple的api,官方有个SampleCode,叫做CryptoExercise,里面有对RSA大部分操作的封装,可以简单使用和验证,但是非对称使用的时候(加密解密/签名验证 不是都用这些api配到使用的话)要注意一些参数的传递,例如SecPadding,openssl默认是paddingNone的。
在App上使用
这样的字符串一般会放在一个.pem的文件里面,可以供openssl直接使用,但是正常来说,pem明显是不适用的,而且因为是明文,安全性不高
因此我们可以通过以下方法来生成一个证书对,也可以给其他方通用
- Create a certificate signing request with the private key
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr
- Create a self-signed certificate with the private key and signing request
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt
- Convert the certificate to DER format: the certificate contains the public key
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der
- Export the private key and certificate to p12 file
openssl pkcs12 -export -out rsaPrivate.p12 -inkey rsaPrivate.pem -in rsaCert.crt
从这里你会发现,Publickey是可以从Privatekey生成的,因为他们的modulus是一样的,而Publickey的exponent基本上固定是e(65537), 而且得到的结果是一个p12,是不是很熟悉?和我们开发者证书gen给其他人用的时候是一样的
得到这2个证书之后,就可以利用oc的代码来获取到SecKeyRef的对象,进行相关的操作了,具体可以看我封装的RSAKit(https://github.com/IvanChan/RSAKit),如果后续有需要使用RSA加解密和签名的话,也可以用这个通用接口来实现。
签名机制
再来回想一下,RSA用公钥encrypt,私钥decrypt,个人猜测,这样的做法是为了让个人信息在本地保存,外部不知道个人隐私信息,例如开发者证书。
回到我们的实际用途上,我们肯学希望用的是私钥在服务器加密,公钥在客户端解密,这样可以保证证书不会在客户端被他人破解获取到,伪造一些数据。所以我们使用RSA的签名机制,用私钥sign,公钥verify。
所谓的签名,就像你把数据给别人之前,先打一个胎记,然后到使用这个数据的时候,看一下有没有这个胎记,才认这份数据。
简单看一下流程
- A有一个Data,一个私钥Privatekey
- Data —— 通过Privatekey ——> 生成Signature
- 把Data和Signature一起发给B
- B拿到一个Data和Signature,同时B手上有一个Publickey
- 那么B在真正使用Data之前,先用 Publickey + Signature —> 对Data进行验明正身
- 验证通过后使用Data
参考
https://www.openssl.org/docs/apps/rsa.html https://www.openssl.org/docs/apps/rsautl.html https://www.devco.net/archives/2006/02/13/public_-_private_key_encryption_using_openssl.php