侧边栏壁纸
  • 累计撰写 251 篇文章
  • 累计创建 138 个标签
  • 累计收到 16 条评论

目 录CONTENT

文章目录

JS RSA 公钥解密

Sherlock
2021-01-12 / 0 评论 / 0 点赞 / 3146 阅读 / 6159 字 / 编辑
温馨提示:
本文最后更新于 2023-10-09,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一般情况下,都是公钥负责加密,私钥负责解密。
签名的话,私钥负责签名,公钥负责验证。

偶尔特殊场景下会使用公钥解密,而目前常见的JS 工具都不支持,可以稍加改造让其支持:

1.以 jsencrypt.js 为例

下载https://github.com/travist/jsencrypt/blob/master/bin/jsencrypt.js文件

2. 修改 RSAKey.prototype.decryptthis.doPrivate(c)this.doPublic(c)

RSAKey.prototype.decrypt = function (ctext) {
	var c = parseBigInt(ctext, 16);
	var m = this.doPublic(c);
	//var m = this.doPrivate(c);
	if (m == null) {
		return null;
	}
	return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
};

3.修改pkcs1unpad2

// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
function pkcs1unpad2(d, n) {
    var b = d.toByteArray();
    var i = 0;
    while (i < b.length && b[i] == 0) {
        ++i;
    }
    /** 注释掉该判断即可
    if (b.length - i != n - 1 || b[i] != 2) {
        return null;
    }
    */
    ++i;
    while (b[i] != 0) {
        if (++i >= b.length) {
            return null;
        }
    }
    var ret = "";
    while (++i < b.length) {
        var c = b[i] & 255;
        if (c < 128) { // utf-8 decode
            ret += String.fromCharCode(c);
        }
        else if ((c > 191) && (c < 224)) {
            ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
            ++i;
        }
        else {
            ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
            i += 2;
        }
    }
    return ret;
}

4.测试验证

var verify = new JSEncrypt();
verify.setPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp68sRm+5Yrw0BfbCUcWChcXeM8ku/Ihow8cQDwBA3WlVHMGiC5XeAsE7sjeG/8l5Al+3FEGluMSfKu9dgYkjVmrSQMHEnIIth37/xeLAkQJfLRQRB+vfzUtQpz1tasuz0TxLq9jimGVaei3OlsZ0D4HnjzTpvJsfncZ3R6zmGRwIDAQAB");
//使用私钥加密后的数据
var verified = verify.decrypt("nKczrFFWcL4NaFdPZeYtlxtwxWNZ4tf+fi5+PT9fsbHFX/tQs818xwXYcFK5RFMHcBUX3Wx7uPm6zoG4orEQyABmQfxvJpqibT6izx6KwvW02R8MrfNRkwohALDm3lFnedrH4j9DswDKlW6IlNWGbP9Y7by/ji/ZUDtY8f6YxkM=");
// 123abc!@#[]90
console.log('解密后数据:', verified);

5.对应的服务端需要使用私钥加密

这里使用 hutools 工具包,操作简单

String content = "123abc!@#[]90";
String privateKey = "MIICXQIBAAKBgQCp68sRm+5Yrw0BfbCUcWChcXeM8ku/Ihow8cQDwBA3WlVHMGiC5XeAsE7sjeG/8l5Al+3FEGluMSfKu9dgYkjVmrSQMHEnIIth37/xeLAkQJfLRQRB+vfzUtQpz1tasuz0TxLq9jimGVaei3OlsZ0D4HnjzTpvJsfncZ3R6zmGRwIDAQABAoGAc4HMCu5auYJVbvwH+wCPVW+SeweCIi2GbFsMihgXbelEKx5nDQP7lklDfV59ZUPWekZM5Tbg1H0ptZC3OdfV9fINdq6hXLzJ6+StWCn1Cm6IoHE4bSSLN8WZw/cNpOSfy9/D6fn4+P71TfHmdAbIpihLqDDcYKYI1VjxsIwokuECQQDea7SwBGM0lEbLx3cOx+PkjMTiVdpzA9mUCy1qzXQ9zPVPgOEF3L6sxdeb9LH/wF978DlmP7Im4/pYS6Gn1lOZAkEAw5MJ4ruzatdHT1uZEZoTZwwH23/D0sq8i9RAUkttPg85sa+E43VvauucOMJ143XkWB1+VhT3qt7K3/pdPCzU3wJBAMb6yCzn3/B77lID4ikg/mnK3aA2ejWG7jGZRkpT/3gCUjuL6sOZ1iMu41KsOEm6yjFQmak1tqVAMwimsLo77ZECQH6WyitMO1HIjou7SlSuWii1DNDy3hmuyGPpusiJsegdisaMSYPBG6ElaDUFCbF1OZwfWwr0/2pGUyKut3KfwhkCQQCa2unWiG9HT9Oe1YUSZTT9wExTKILgdXK5UPRvKerxwinZmhCU039QwlfO3tA6bW4BV+ydpILRPR8rrMPyL/HQ";
AsymmetricCrypto privateRsaCrypto = new AsymmetricCrypto(AsymmetricAlgorithm.RSA_ECB_PKCS1, privateKey, null);
byte[] serverSrcEncrypt = privateRsaCrypto.encrypt(StrUtil.bytes(content, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
String base64ServerSrcEncrypt = Base64.encode(serverSrcEncrypt);
System.out.println(base64ServerSrcEncrypt);

6.注意事项

RSA 算法特性

  • 明文长度不能超过密钥长度,且加密后的密文长度等于密钥长度
  • 私钥加密 公钥可以解密,公钥加密 私钥可以解密
  • 私钥每次加密结果相同,公钥每次加密结果不同

RSA 加密可能会有数据过长问题

  • 当加解密的数据长度过长时,需要对数据进行分割,否则会抛出 ArrayIndexOutOfBoundsException: too much data for RSA block
  • 由于PKCS#1建议的 padding 占用了 11 个字节;
  • 对于1024位长度的密钥来说,最多只支持加密(1024/8)-11 = 117字节长度的数据;
  • 对于2048位长度的密钥来说,则是(2048/8)-11 = 245字节;

7.Java 服务端参考

可以参考 hutools AsymmetricCrypto 类
使用AsymmetricAlgorithm.RSA_ECB_PKCS1 算法(用了默认补位方式为RSA/ECB/PKCS1Padding)
需要配置decryptBlockSizeencryptBlockSize,1024位密钥推荐117128

/**
 * 分段加密或解密
 * 
 * @param data 数据
 * @param maxBlockSize 最大分段的段大小,不能为小于1
 * @return 加密或解密后的数据
 * @throws IllegalBlockSizeException 分段异常
 * @throws BadPaddingException padding错误异常
 * @throws IOException IO异常,不会被触发
 */
private byte[] doFinalWithBlock(byte[] data, int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException {
	final int dataLength = data.length;
	@SuppressWarnings("resource")
	final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
	
	int offSet = 0;
	// 剩余长度
	int remainLength = dataLength;
	int blockSize;
	// 对数据分段处理
	while (remainLength > 0) {
		blockSize = Math.min(remainLength, maxBlockSize);
		out.write(cipher.doFinal(data, offSet, blockSize));

		offSet += blockSize;
		remainLength = dataLength - offSet;
	}
	
	return out.toByteArray();
}
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区