RSA也是一个块加密算法( block cipher algorithm),总是在一个固定长度的块上进行操作。但跟AES等不同的是, block length是跟key length有关的。每次RSA加密的明文的长度是受RSA填充模式限制的,但是RSA每次加密的块长度就是key length。

RSA加密图如下:

java RSA执行 OAEP 填充模式的公钥 RSA rsa加密填充_后端

 m:代表明文,e:exponent,n:modulus

c:代表密文,d:密钥大数

e:int类型,n,d:big.int类型

假如你选择的秘钥长度为1024bit共128个byte:

1.当你在客户端选择RSA_NO_PADDING填充模式时,如果你的明文不够128字节加密的时候会在你的明文前面,前向的填充零。解密后的明文也会包括前面填充的零,这是服务器需要注意把解密后的字段前向填充的零去掉,才是真正之前加密的明文。

2.当你选择RSA_PKCS1_PADDING填充模式时,如果你的明文不够128字节加密的时候会在你的明文中随机填充一些数据,所以会导致对同样的明文每次加密后的结果都不一样。对加密后的密文,服务器使用相同的填充方式都能解密。解密后的明文也就是之前加密的明文。

3.RSA_PKCS1_OAEP_PADDING填充模式没有使用过, 他是PKCS#1推出的新的填充方式,安全性是最高的,和前面RSA_PKCS1_PADDING的区别就是加密前的编码方式不一样。

因此:同样的密钥加密同一个数据,加密的结果是不一样的。(一个优秀的加密必须每次生成的密文都不一致,即使每次你的明文一样、使用同一个公钥。因为这样才能把明文信息更安全地隐藏起来。

然而,为了适配一些老的接口,RSA加密就采用NO_PADDING模式,该模式由于没有添加随机数,因此同一个密钥密码加密同一个数据,每次执行结果是一样的。

NO_PADDING代码如下:

特别说明:golang原始的

//对明文进行加密 cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)

是对明文进行了填充,及时将:rand.Reader 改为nil,底层也会进行填充。原生Golang库没有提供no_padding模式模式。如果要实现no_padding模式,可以采用如下代码。

1、golang版本

package main

import (
	"crypto/rsa"
	"fmt"
	"math/big"
)

//var RSA_PublicKeyString  = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"
var RSA_PublicKeyString = "cb4c3ebfc7bfabf880371711b424b8b4187ddc7b3cf3656e42310df1676bf9972fa472fc6a49daf516a150f971e6307f4f2a06e35845ea3e23bae6183f22896d373e4ff8372652abd3ec74d57b17595e59d05fa1c507a55e593dc48ed80a258adcfeb4068e5f7830ae5e5103193f26d8ab9134e2968b68db25709f3f9aaa477f"
var Public_RSA_Key = rsa.PublicKey{
	N: fromBase(RSA_PublicKeyString),
	E: 65537,
}

func fromBase(base string) *big.Int {
	i, ok := new(big.Int).SetString(base, 16)  //如果 base参数的进制表示形式(如果base是10进制,此处将16改为10)
	if !ok {
		panic("Bad number: %s" + base)
	}
	return i
}
func encrypt_RSA(pub *rsa.PublicKey, data []byte) []byte {
	encrypted := new(big.Int)
	e := big.NewInt(int64(pub.E))
	payload := new(big.Int).SetBytes(data)
	encrypted.Exp(payload, e, pub.N)
	return encrypted.Bytes()
}
func main() {
	packet := []byte("jiangzhou")
	encrypted := encrypt_RSA(&Public_RSA_Key, packet)
	fmt.Printf("packet: %x \n encrypted: %x \n %d", packet, encrypted, len(encrypted))
}

运行效果:

java RSA执行 OAEP 填充模式的公钥 RSA rsa加密填充_开发语言_02

2、Python版本

import math

if __name__ == '__main__':
    # 实为16进制串,前补0
    e = '010001'
    # m也需要补00
    m = 'cb4c3ebfc7bfabf880371711b424b8b4187ddc7b3cf3656e42310df1676bf9972fa472fc6a49daf516a150f971e6307f4f2a06e35845ea3e23bae6183f22896d373e4ff8372652abd3ec74d57b17595e59d05fa1c507a55e593dc48ed80a258adcfeb4068e5f7830ae5e5103193f26d8ab9134e2968b68db25709f3f9aaa477f'
    m = int.from_bytes(bytearray.fromhex(m), byteorder='big')
    e = int.from_bytes(bytearray.fromhex(e), byteorder='big')
    # js加密为反向,为保持一致原文应反向处理,所以这里原文实际为204dowls
    plaintext = 'jiangzhou'.encode('utf-8')
    # 无填充加密逻辑
    input_nr = int.from_bytes(plaintext, byteorder='big')
    crypted_nr = pow(input_nr, e, m)
    keylength = math.ceil(m.bit_length() / 8)
    crypted_data = crypted_nr.to_bytes(keylength, byteorder='big')
    print(crypted_data.hex())

运行效果:

java RSA执行 OAEP 填充模式的公钥 RSA rsa加密填充_5e_03

 由于没有添加随机数,可以看出 go版本与Python版本通过相同的密钥加密相同的数据,结果一样。

以上代码的公钥不是传统的pem文件形式,而是通过modulus、exponent构造出来的。特别注意:go语言中:exponent为:65537,Python语言为:010001。