非对称加密技术是保证我们信息安全非常重要的技术。 RSA便是其中翘楚, 应用广泛。本文仅从实用的角度, 总结一下不同语言中, RSA加解密的实现。
零、在线加解密工具
一、 基础
1.1. 基本原理
请参考这两篇文章: RSA算法原理(一),RSA算法原理(二)
1.2. 主要参数
p 大质数A q 大质数B n 模, 大质数之积, 难于分解。 n = P * q, n的二进制位数为秘钥长度。 φ(n) 小于n的质数个数。 φ(n) = (p - 1) * (q - 1) e 公钥指数, 通常为65537。(n, e)共同构成公钥。 d 私钥指数。(n, d)共同构成私钥。
1.3 公/私钥显示
我们拿到的公钥或是私钥, 通常是经过Base64编码后得到的。想要了解Base64, 请参考文章Base64笔记,它的作用主要是将二进制秘钥转换为可打印/显示的文本字符。
1.4 秘钥格式
PKCS#1定义了RSA公钥函数的基本格式标准,特别是数字签名。它定义了数字签名如何计算,包括待签名数据和签名本身的格式;它也定义了PSA公/私钥的语法。PKCS#8:私钥信息语法标准。PKCS#8定义了私钥信息语法和加密私钥语法,其中私钥加密使用了PKCS#5标准。
-----BEGIN RSA PRIVATE KEY----- 以此开头的秘钥格式为PKCS#1 -----BEGIN PRIVATE KEY----- 以此开头的秘钥格式为PKCS#8
现在一般都用PKCS#8 格式证书较多,本文的示例代码都在使用PKSC#8。 如果是PKCS#1格式证书, 可以利用工具转换为PKCS#8 。本工具只转换私有证书格式。
相关文章: PKI常见证书格式和转换
1.5 填充模式
1)RSA_PKCS1_PADDING 填充模式,最常用
输入 必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11。如果输入的明文过长,必须切割,然后填充。
输出 和modulus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2)RSA_PKCS1_OAEP_PADDING填充模式
同上, 但长度限制以及切割尺寸为RSA_size(rsa) – 41。
3)RSA_NO_PADDING填充模式
不填充。
1.6 字符编码格式
明文加密前需要转为字节数组进行加密。密文解密后得到的字节数组恢复为字符串时,也需要了解字符串的编码方式。 这两个过程中的编码格式(例如UTF8)必须一致, 否则我们将得到乱码。
1.7 测试秘钥
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfIQJvvGUTHpGHgUURtPduR7F4 j3r6ijyDDbtHlPFlVUe5fOFP53DAr8WGb2yL1iHhXMhipqvIJvwGQlKVFYdUkxht jBzghUciSi54Iragb8eQvatND2O12oOKcyj1u53wfIRh2HIUnmk4DsJXWkHz2yDo EUi1zk7OTmyIUGBcHQIDAQAB -----END PUBLIC KEY----- -----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJ8hAm+8ZRMekYeB RRG0925HsXiPevqKPIMNu0eU8WVVR7l84U/ncMCvxYZvbIvWIeFcyGKmq8gm/AZC UpUVh1STGG2MHOCFRyJKLngitqBvx5C9q00PY7Xag4pzKPW7nfB8hGHYchSeaTgO wldaQfPbIOgRSLXOTs5ObIhQYFwdAgMBAAECgYBmnE6J5p32mjuVeVIkfOEgh7J0 33CZphInH++n1O9kPH2nqlNPxxn55l4ktiz0bvwh6kZ89qTGx/yzvIisAdnlNzM1 5u8qdJq9ekY1y/yjhMXWZkQNB/Srqqi8z/03Po410WlMs8xSkCfR3jymy+kdIdHe /wUet0NrX3lpMR2uMQJBAMxTaVrBK6skvmmGkKYJdM5xJ8td1w6vvernwufe+RTD vQgaTHwYdjhtu03IykPQ9quzlzjltbrAJ6gKO+eAopcCQQDHX279Zo2yPjHVofec jLQ2J+Q7ooQOgRwqUVIqO1TzY5zJ+q5S92uuGn2dGQVXq+/EjOI/8vhYKpXdVcRc nrFrAkBmFVpyvEY8CGyVvmRwgBL6vXAbW/QBY25oeYhYXn9lj3HXJYSHD5WpFbHZ 5/KeNT24hU6NX78O4cguXYaWYB9bAkBPUVddtuQeioHXBSX2gT/c38f8Na6l+pWS /GFc7dfhMLsa3AFofoi3blCVJHQsqfmsz4PffzFwGhkfQlTpqDTbAkEAnrKYNlxM G5YUR6hLfPbHz3lan2ePVeQy7HtZW9LUhhGDfV7JkIc872fnJpehEGtqpnQq08Po ZaH/vvPTwPB1tQ== -----END PRIVATE KEY-----
二、Java实现
2.1 参考文章 java中RSA加解密的实现, java RSA加解密实现
2.3 RSAUtils.java下载
2.2 测试代码
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
public class Test {
public static void main(String[] args) throws Exception {
HashMap<String, Object> map = RSAUtils.getKeys();
// 秘钥获取方式一:随机生成公钥和私钥对
RSAPublicKey publicKeySample = (RSAPublicKey) map.get(RSAUtils.KEY_PUBLIC);
RSAPrivateKey privateKeySample = (RSAPrivateKey) map.get(RSAUtils.KEY_PRIVATE);
// 秘钥获取方式二:从文件/字符串获取公钥私钥
String publKey = "xxx"; // base64加密后的公钥, 1.7节中有公钥文本的样例。
String privateKeyStr = "xxx"; //base64加密后的私钥, 1.7节中有私钥文本的样例。
RSAPublicKey rsaPublicKey = RSAUtils.parsePublicKey(publKey);
RSAPrivateKey privateK = RSAUtils.parsePrivateKey(privateKeyStr);
String encriptedStr = RSAUtils.encryptByPublicKey("I'm encript by rsa public key!", rsaPublicKey);
Test.mPrint("公钥加密密文:" + encriptedStr);
String result = RSAUtils.decryptByPrivateKey(encriptedStr, privateK);
Test.mPrint("私钥解密:" + result);
encriptedStr = RSAUtils.encryptByPrivateKey("I'm encript by rsa private key!", privateK);
Test.mPrint("私钥加密密文:" + encriptedStr);
result = RSAUtils.decryptByPublicKey(encriptedStr, rsaPublicKey);
Test.mPrint("公钥解密:" + result);
}
public static void mPrint(String msg) { System.out.println(msg); }
}
三、 Javascript实现
3.1 参考文章,
Jsencript项目Github, 此库仅支持PKCS#1, 不符合需求。需要其它库的辅助完成任务。
Js Rsa签名实现, 此文提到了keyutil-1.0.js,这个工具源自jsrsasign项目 。通过这个工具, 我们可以解析PKCS#8格式的公钥证书。keyutil api文档。
3.2 测试代码
私钥加密, 公钥解密似乎跑不通, 好在客户端暂时没有遇到这样的需求。
<textarea id="public_key">参见1.7节测试公钥</textarea> <textarea id="private_key">参见1.7节测试私钥</textarea> <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script> <script src="jsrsasign/jsrsasign-latest-all-min.js"></script> <script src="jsrsasign/rsapem-1.1.js"></script> <script src="jsrsasign/rsasign-1.2.js"></script> <script src="jsrsasign/asn1hex-1.1.js"></script> <script src="jsrsasign/crypto-1.1.js"></script> <script src="jsrsasign/pkcs5pkey-1.0.js"></script> <script src="jsrsasign/base64x-1.1.js"></script> <script src="jsrsasign/keyutil-1.0.js"></script> <script src="bin/jsencrypt.js"></script> <script> $(document).ready(function() { var publicKey = document.getElementById("public_key").value; var privateKey = document.getElementById("private_key").value; // 解析公钥证书 var pubKey = KEYUTIL.getKeyFromPublicPKCS8PEM(publicKey); // 解析私钥证书 var privKey = KEYUTIL.getKeyFromPlainPrivatePKCS8PEM(privateKey); // 公钥加密 var encrypt = new JSEncrypt(); encrypt.setPublicKey(pubKey); var encrypted = encrypt.encrypt("I'm encrypt by public key!"); // 私钥解密 var decrypt = new JSEncrypt(); decrypt.setPrivateKey(privKey); console.log("公钥加密密文 : " + encrypted); var uncrypted = decrypt.decrypt(encrypted); console.log("私钥解密后,原文 : " + uncrypted); }); </script>
四、Python实现
请参考文章python rsa加密解密
五、 php实现
请参考文章php rsa加密解密实例