多次被问到这样的问题:
java服务端的rsa加密操作已经完成,返回一个16进制的字符串给python平台,但是在python进行私钥解密的时候发现行不通。。。。
前端python加密,后端用java解密,解不出来
还有诸如nodejs
从理论上来说,rsa加密的基础都是一样的,不存在一个语言加密,另一个语言解密不出来的情况。那出问题的只能是我们使用的方法不对。
java vs python
声明:java的加密解密模块需要更加精细的算法细节指定
java的加密方式
javax.crypto.Cipher,定义的获取方式
static Cipher getInstance(String transformation)Returns a Cipher object that implements the specified transformation.static Cipher getInstance(String transformation, Provider provider)Returns a Cipher object that implements the specified transformation.static Cipher getInstance(String transformation, String provider)Returns a Cipher object that implements the specified transformation.
有两个重要参数:
1. transformation定义为
A transformation is a string that describes the operation (or set of operations) to be performed on the given input, to produce some output. A transformation always includes the name of a cryptographic algorithm (e.g., AES), and may be followed by a feedback mode and padding scheme.A transformation is of the form:"algorithm/mode/padding" or"algorithm"(in the latter case, provider-specific default values for the mode and padding scheme are used). For example, the following is a valid transformation: Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
transformation有以下几种:
Every implementation of the Java platform is required to support the following standard Cipher transformations with the keysizes in parentheses:AES/CBC/NoPadding (128)AES/CBC/PKCS5Padding (128)AES/ECB/NoPadding (128)AES/ECB/PKCS5Padding (128)DES/CBC/NoPadding (56)DES/CBC/PKCS5Padding (56)DES/ECB/NoPadding (56)DES/ECB/PKCS5Padding (56)DESede/CBC/NoPadding (168)DESede/CBC/PKCS5Padding (168)DESede/ECB/NoPadding (168)DESede/ECB/PKCS5Padding (168)RSA/ECB/PKCS1Padding (1024, 2048)RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)These transformations are described in the Cipher section of the Java Cryptography Architecture Standard Algorithm Name Documentation. Consult the release documentation for your implementation to see if any other transformations are supported.
2.provider
可以通过Security.getProviders()查看
java.security.Provider [] providers=Security.getProviders(); for(int i=0;i
具体的provider如下:
SUNSunRsaSignSunECSunJSSESunJCESunJGSSSunSASLXMLDSigSunPCSCSunMSCAPI
python的加密方式需要到具体的代码里面了,如
from crypto.PublicKey import RSAfrom crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5# from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5def rsaEncrypt(message): key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYLCumWz6MGHmAMLIaPt3SItIhMYHuyLn48muQz2xKj9PVqETGfjq/GTxHE3wfvGCEs/JXY1rV4uysUuAS/xwZuyJ9j+sB599lzmpxdhIWu/jGMR0h86nnpNUcssYwR3Bww3oU5+dYEtGpfOytMyh3eJeUZiNNBXqH+IaSYfU3hwIDAQAB' key1=base64.b64decode(key) rsaKey=RSA.importKey(key1) cipher=Cipher_pkcs1_v1_5.new(rsaKey) temp=cipher.encrypt(message) return binascii.b2a_hex(temp)if __name__ == '__main__': rsaEncrypt(13950346593)
进入encypt方法中:
def encrypt(self, message): """Produce the PKCS#1 v1.5 encryption of a message. This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and it is specified in `section 7.2.1 of RFC8017 `_. :param message: The message to encrypt, also known as plaintext. It can be of variable length, but not longer than the RSA modulus (in bytes) minus 11. :type message: bytes/bytearray/memoryview :Returns: A byte string, the ciphertext in which the message is encrypted. It is as long as the RSA modulus (in bytes). :Raises ValueError: If the RSA key length is not sufficiently long to deal with the given message. """
发现其支持的是
PKCS#1 v1.5 encryption
对应java的模式是:
RSA/ECB/PKCS1Padding (1024, 2048)
IvParameterSpec
This class specifies an initialization vector (IV). Examples which use IVs are ciphers in feedback mode, e.g., DES in CBC mode and RSA ciphers with OAEP encoding operation.
java和nodejs
用NODE RSA JS 加密解密正常,用JAVA RSAUtils工具类加密解密正常。但是用node加密玩的java解密不了。原因:node默认的是
DEFAULT_ENCRYPTION_SCHEME = 'pkcs1_oaep' 而java中默认的是pkcs1。
node-rsa源码:https://github.com/rzcoder/node-rsa/blob/ea5c17d9351c857c0594d7921c596ff5636882f1/src/NodeRSA.js
var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1_oaep';
node-rsa官方文档:https://www.npmjs.com/package/node-rsa
Options
You can specify some options by second/third constructor argument, or over key.setOptions()method.
environment — working environment (default autodetect):'browser' — will run pure js implementation of RSA algorithms.'node' for nodejs >= 0.10.x or io.js >= 1.x — provide some native methods like sign/verify and encrypt/decrypt.encryptionScheme — padding scheme for encrypt/decrypt. Can be 'pkcs1_oaep' or 'pkcs1'. Default 'pkcs1_oaep'.signingScheme — scheme used for signing and verifying. Can be 'pkcs1' or 'pss' or 'scheme-hash' format string (eg 'pss-sha1'). Default 'pkcs1-sha256', or, if chosen pss: 'pss-sha1'.
Notice: This lib supporting next hash algorithms: 'md5', 'ripemd160', 'sha1', 'sha256', 'sha512' in browser and node environment and additional 'md4', 'sha', 'sha224', 'sha384' in node only.
所以要保持一致:
import NodeRSA from 'node-rsa';const rsa_encrypt = (data) => { let key = new NodeRSA('-----BEGIN PUBLIC KEY-----' + 'MIGfMA0。。。。。。。AQAB' + '-----END PUBLIC KEY-----'); // key.generateKeyPair(1024); key.setOptions({encryptionScheme: 'pkcs1'}) let encryptKey = key.encrypt(data, 'base64') return encryptKey;}
后台:
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateK); int inputLen = encryptedData.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(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; }
参考文献:
【1】https://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html#getInstance(java.lang.String)
【2】https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher
【3】https://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/IvParameterSpec.html
【4】https://blog.csdn.net/mshootingstar/article/details/56496719