多种语言使用RSA

非对称加密技术是保证我们信息安全非常重要的技术。 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加密解密实例