如何实现用javascript实现rsa加解密

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现用javascript实现rsa加解密相关的知识,希望对你有一定的参考价值。

javascript实现rsa加解密的实现方式是通过PKCS完成的。
1、整个定义的function

function pkcs1pad2(s,n)
if(n < s.length + 11) // TODO: fix for utf-8
alert("Message too long for RSA");
return null;

var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0)
var c = s.charCodeAt(i--);
//UTF-8编码为变长字节,使用实际的字节来记录
if(c < 128) // encode using utf-8
ba[--n] = c;

else if((c > 127) && (c < 2048))
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;

else
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;


//实际输入拼装结束,将下一位赋值为0标记结束
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
//拼接随机非0字节
while(n > 2) // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];

//这两位做简单的校验
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);

该方法中对UTF-8字符进行了兼容,并且在拼装完实际输入的字符后,还拼装了随机的字节,使用拼装后的字符串去加密。由于每次拼装的结果是随机的,这样每次加密后的密文都不同。
2、调用方法:;
function RSAEncrypt(text)
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
参考技术A 参考下面代码:
function RSAPowMod(txt, key, n)
var a, b;
if (key % 2 == 0)
a = 1
for (var i = 1; i <= key / 2; i++)
b = (txt * txt) % n
a = (b * a) % n

else
a = txt
for (var i = 1; i <= key / 2; i++)
b = (txt * txt) % n
a = (b * a) % n


return a;
本回答被提问者采纳
参考技术B 具体实现思路如下:
1。服务端生成公钥与私钥,保存。
2。客户端在请求到登录页面后,随机生成一字符串。
3。后此随机字符串作为密钥加密密码,再用从服务端获取到的公钥加密生成的随机字符串。
4。将此两段密文传入服务端,服务端用私钥解出随机字符串,再用此私钥解出加密的密文。
这其中有一个关键是解决服务端的公钥,传入客户端,客户端用此公钥加密字符串后,后又能在服务端用私钥解出。
此文即为实现此步而作。
加密算法为RSA:
1。服务端的RSA Java实现。
2.测试页面:
IndexAction.java
通过此action进入登录页面,并传入公钥的 Modulus 与PublicExponent的hex编码形式。
3。登录页面 login.jsp
点击登录后,调用LoginAction.java
因为发现解出的明文是倒序的,后面就用StringBuffer的reverse()来转换了一下。
4。login.jsp所调用的js

js.rar (6.4 KB)
描述: login.jsp所调用的javascript,有:RSA.jsBigInt.jsBarrett.js
参考技术C 用javascript实现rsa加解密的实现方式是通过PKCS完成的。
1、整个定义的function

function pkcs1pad2(s,n)
if(n < s.length + 11) // TODO: fix for utf-8
alert("Message too long for RSA");
return null;

var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0)
var c = s.charCodeAt(i--);
//UTF-8编码为变长字节,使用实际的字节来记录
if(c < 128) // encode using utf-8
ba[--n] = c;

else if((c > 127) && (c < 2048))
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;

else
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;


//实际输入拼装结束,将下一位赋值为0标记结束
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
//拼接随机非0字节
while(n > 2) // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];

//这两位做简单的校验
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);

该方法中对UTF-8字符进行了兼容,并且在拼装完实际输入的字符后,还拼装了随机的字节,使用拼装后的字符串去加密。由于每次拼装的结果是随机的,这样每次加密后的密文都不同。
2、调用方法:;
function RSAEncrypt(text)
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;

RSA 加解密(Java 实现)

  RSA 算法是一种非对称加解密算法。服务方生成一对 RSA 密钥,即公钥 + 私钥,将公钥提供给调用方,调用方使用公钥对数据进行加密后,服务方根据私钥进行解密。

一、基础工具类

  下方工具类涵盖了生成 RSA 密钥对、加密、解密的方法,并附上了测试过程。

package com.test.utils;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class RSAUtil 

    public static final String KEY_ALGORITHM = "RSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";

    private static final String PRIVATE_KEY = "RSAPrivateKey";

	// 1024 bits 的 RSA 密钥对,最大加密明文大小
    private static final int MAX_ENCRYPT_BLOCK = 117;

    // 1024 bits 的 RSA 密钥对,最大解密密文大小
    private static final int MAX_DECRYPT_BLOCK = 128;

    // 生成密钥对
    public static Map<String, Object> initKey(int keysize) throws Exception 
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        // 设置密钥对的 bit 数,越大越安全
        keyPairGen.initialize(keysize);
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 获取公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    

    // 获取公钥字符串
    public static String getPublicKeyStr(Map<String, Object> keyMap) 
        // 获得 map 中的公钥对象,转为 key 对象
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    

    // 获取私钥字符串
    public static String getPrivateKeyStr(Map<String, Object> keyMap) 
        // 获得 map 中的私钥对象,转为 key 对象
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    

    // 获取公钥
    public static PublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException 
        byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    

    // 获取私钥
    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception 
        byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    

    /**
     * BASE64 编码返回加密字符串
     *
     * @param key 需要编码的字节数组
     * @return 编码后的字符串
     */
    public static String encryptBASE64(byte[] key) 
        return new String(Base64.getEncoder().encode(key));
    

	/**
     * BASE64 解码,返回字节数组
     *
     * @param key 待解码的字符串
     * @return 解码后的字节数组
     */
    public static byte[] decryptBASE64(String key) 
        return Base64.getDecoder().decode(key);
    

    /**
     * 公钥加密
     *
     * @param text         待加密的明文字符串
     * @param publicKeyStr 公钥
     * @return 加密后的密文
     */
    public static String encrypt1(String text, String publicKeyStr) 
        try 
            log.info("明文字符串为:[]", text);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
            byte[] tempBytes = cipher.doFinal(text.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(tempBytes);
         catch (Exception e) 
            throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e);
        
    

    /**
     * 私钥解密
     *
     * @param secretText    待解密的密文字符串
     * @param privateKeyStr 私钥
     * @return 解密后的明文
     */
    public static String decrypt1(String secretText, String privateKeyStr) 
        try 
            // 生成私钥
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
            // 密文解码
            byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes("UTF-8"));
            byte[] tempBytes = cipher.doFinal(secretTextDecoded);
            return new String(tempBytes);
         catch (Exception e) 
            throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e);
        
    

    public static void main(String[] args) throws Exception 
        Map<String, Object> keyMap;
        String cipherText;
        // 原始明文
        String content = "春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。";

        // 生成密钥对
        keyMap = initKey(1024);
        String publicKey = getPublicKeyStr(keyMap);
        log.info("公钥:[],长度:[]", publicKey, publicKey.length());
        String privateKey = getPrivateKeyStr(keyMap);
        log.info("私钥:[],长度:[]", privateKey, privateKey.length());

        // 加密
        cipherText = encrypto(content, publicKey);
        log.info("加密后的密文:[],长度:[]", cipherText, cipherText.length());

        // 解密
        String plainText = decrypt1(cipherText, privateKey);
        log.info("解密后明文:[]", plainText);
    


  运行上述 main 函数,输出如下所示,可见加解密成功。

公钥:[MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVk5GORH7kMgZBI7xN6dvlQ6NXO8/emNe/KOxFJRI1r49ecxSb41IBV7rcf+5z7hihbhXqUm65d3O0xcqDOqRYI0LgBxERsX0cPcAl7zSGDgynUC8jlTxBWlQjwb758vvmDRX3g5v0btUPObcyB1IgM3cpVDmsb5Obcl0Wxk2gGwIDAQAB],长度:[216]
私钥:[MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJWTkY5EfuQyBkEjvE3p2+VDo1c7z96Y178o7EUlEjWvj15zFJvjUgFXutx/7nPuGKFuFepSbrl3c7TFyoM6pFgjQuAHERGxfRw9wCXvNIYODKdQLyOVPEFaVCPBvvny++YNFfeDm/Ru1Q85tzIHUiAzdylUOaxvk5tyXRbGTaAbAgMBAAECgYBFLe5JNYbWtghMgmGqS3onrEPUqdY3ZsuKHzw/sqicaelOTr1+aAHamx/Ssdywg7Oing7GxH9vij7aogxK64lsf/OD9Eq2DCvpbNValV/GVABRcCQLO6neHWujen3ex13ky1qtqCS6oZgbqDZyFAj5EGydJdbAgGTZ/rU0Mqbn+QJBANzi3i7a14KXRJYXx7r1UcTVbs30P0jk2F5SaPYsM0UW4Ec5qmqCv6mCYn6GHCJnlJiHvYt9QOUvWB9xuwHtXg0CQQCtWrITaT7P/jHqxzVc02nM420sNtOCrkSy60gZtfNSWFtTl/ddz2ACrN+fbfolUJhHaus3+ekyFoqpIdfNkpTHAkEAlrwO6SSYWtrFiDOULiZI9ay837klEqZwbPWKASwqlKRGyvQ0MlklWBTNCBCW1Heg9PH0zGLeTUggt9yRxH/qZQJAGaGhVtFm4iX5h3cw4qq3p/2wdKseluHhcnrrTDHk6jX6Ot/rSUmSLpMU9WOzarUB7v1WDg67dhZzJhLE77ZOnQJAAe/5aqiYn2SvAxMislav4PcNnbqnHVH+mmRlRP0xeuOKQQzAEn2nqYBY7vCUw3+SYGxe+EbNMLnrcHvTpQKPsw==],长度:[848]
明文字符串为:[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。]
加密后的密文:[iQIlICQHZJUUxslhyUJSjEdTWm+/B/OWhWXZHhX+ji5Zf7FjaNHr2BCb92ZOmA9BxsAPsS3bg1SajGyBP8fYIkKJpiqHuQ70Jt9xgwdTnaazJEw3BXWRIJIGwniKcEHZZQe+PjjZ4XNGaNSqCa8NtbuJJ6dfFYnv/T/6ANdBwyA=],长度:[172]
解密后明文:[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。]
二、异常及原因分析

  当原始明文长度较小时,使用上述工具类加解密,没有问题。但是当明文长度过长时,会出现加密异常,如下所示。

Exception in thread "main" java.lang.RuntimeException: 加密字符串[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。江流宛转绕芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月?江月何年初照人?人生代代无穷已,江月年年望相似。]时遇到异常
	at com.test.utils.RSAUtil.encrypto(RSAUtil.java:100)
	at com.test.utils.RSAUtil.main(RSAUtil.java:146)
Caused by: javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
	at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
	at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
	at javax.crypto.Cipher.doFinal(Cipher.java:2165)
	at com.test.utils.RSAUtil.encrypto(RSAUtil.java:97)
	... 1 more

  原因分析如下。
  RSA 算法一次能加密的明文长度与密钥长度成正比,如 RSA 1024 实际可加密的明文长度最大是 1024 bits。如果小于这个长度怎么办?就需要进行数据补齐(padding),因为如果没有 padding,用户则无法确定解密后内容的真实长度。字符串之类的内容问题还不大,以 0 作为结束符,但对二进制数据就很难理解,因为不确定后面的 0 是内容还是内容结束符。
  只要用到 padding,那么就要占用实际的明文长度。对于 1024 bits(1024 / 8 = 128 byte[] ) 密钥对而言,如果 padding 方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 字节用于填充),则明文长度最多为 128 - 11 = 117 bytes,于是才有 117 字节的说法,即下面这种常见的说法:len_in_byte(原始明文数据)= len_in_bit(key)/ 8 - 11。
  我们一般使用的 padding 标准有 NoPadding、OAEPPadding、PKCS1Padding 等,其中PKCS1 建议的 padding 就占用了 11 个字节。对于 RSA 加密来讲,padding 也是要参与加密的,所以实际的明文只有 117 字节了。
  我们在把明文送给 RSA 加密器前,要确认这个值是不是大于位长,也就是如果接近位长,那么需要先 padding 再分段加密。除非我们是 “定长定量自己可控可理解” 的加密,不需要 padding。

  各种 padding 对输入数据长度的要求总结如下。

  • 私钥加密时的要求
padding最大数据长度
RSA_PKCS1_PADDINGRSA_size - 11
RSA_NO_PADDINGRSA_size - 0
RSA_X931_PADDINGRSA_size - 2
  • 公钥加密时的要求
padding最大数据长度
RSA_PKCS1_PADDINGRSA_size - 11
RSA_SSLV23_PADDINGRSA_size - 11
RSA_X931_PADDINGRSA_size - 2
RSA_NO_PADDINGRSA_size - 0
RSA_PKCS1_OAEP_PADDINGRSA_size - 2 * SHA_DIGEST_LENGTH - 2
三、分段加解密
	// 分段加密
    public static String encrypt2(String plainText, String publicKeyStr) throws Exception 
        log.info("明文:[],长度:[]", plainText, plainText.length());
        byte[] plainTextArray = plainText.getBytes("UTF-8");
        PublicKey publicKey = getPublicKey(publicKeyStr);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = plainTextArray.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        int i = 0;
        byte[] cache;
        while (inputLen - offSet > 0) 
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) 
                cache = cipher.doFinal(plainTextArray, offSet, MAX_ENCRYPT_BLOCK);
             else 
                cache = cipher.doFinal(plainTextArray, offSet, inputLen - offSet);
            
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        
        byte[] encryptText = out.toByteArray();
        out.close();
        return Base64.getEncoder().encodeToString(encryptText);
    

    // 分段解密
    public static String decrypt2(String encryptTextHex, String privateKeyStr) throws Exception 
        byte[] encryptText = Base64.getDecoder().decode(encryptTextHex);
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        int inputLen = encryptText.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) 
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) 
                cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK);
             else 
                cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
            
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        
        byte[] plainText = out.toByteArray();
        out.close();
        return new String(plainText);
    

  备注:当密钥对改为 2048 bits 时,最大加密明文大小为 2048 (bits) / 8 - 11(byte) = 245 byte,上述代码中的两个常量就变更为:

	// 2048 bits 的 RSA 密钥对,最大加密明文大小
    private static final int MAX_ENCRYPT_BLOCK = 245;

    // 2048 bits 的 RSA 密钥对,最大解密密文大小
    private static final int MAX_DECRYPT_BLOCK = 256;

  PKCS1 和 PKCS8 的区别如下。

  • 以 “-----BEGIN RSA PRIVATE KEY-----” 开头,以 “-----END RSA PRIVATE KEY-----” 结束的就是 PKCS1 格式;
  • 以 “-----BEGIN PRIVATE KEY-----” 开头,以 “-----END PRIVATE KEY-----” 结束的就是 PKCS8 格式。

  通常 JAVA 中需要 PKCS8 格式的密钥。

  在线 RSA 加解密网站:RSA 加解密

文章参考:

以上是关于如何实现用javascript实现rsa加解密的主要内容,如果未能解决你的问题,请参考以下文章

如何实现用javascript实现rsa加解密

用Java实现RSA加解密及签名和验签——.pfx文件格式秘钥

JavaScript实现RSA加解密

RSA 加解密(Java 实现)

PHP如何实现AES加解密

用Java实现RSA加解密及签名和验签——.pem文件格式秘钥