RSA 加密 Javascript 和解密 Java
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23818842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
RSA Encryption Javascript and Decrypt Java
提问by user2498258
Spent almost 2 days with different combinations.I am generating a asymmetric key pair (public and private) in java using RSA algorithm and trying to use the public key in javascript to encrypt some text and decrypt back in java on server side. I am getting "javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes" exception while trying to decrypt back the string encrypted in javascript. Would appreciate some help...
花了将近 2 天的时间使用不同的组合。我正在使用 RSA 算法在 Java 中生成一个非对称密钥对(公共和私有),并尝试使用 javascript 中的公共密钥来加密一些文本并在服务器端用 Java 解密。我在尝试解密用 javascript 加密的字符串时收到“javax.crypto.IllegalBlockSizeException:数据不得超过 128 字节”异常。希望得到一些帮助...
Using thi Javascript library to encrypt.
使用此 Javascript 库进行加密。
https://github.com/wwwtyro/cryptico
https://github.com/wwwtyro/cryptico
var publicKeyString = ""// base64encoded public key string generated in java
var publicKeyString = ""// java生成的base64编码的公钥字符串
Here is my javascript code
这是我的 javascript 代码
var EncryptionResult = cryptico.encrypt("somestring", publicKeyString);
console.log("Encrypted status-"+EncryptionResult.status);
console.log("Encrypted String-"+EncryptionResult.cipher);
It is successfully encrypting the string.
它正在成功加密字符串。
Java Key Generation and Decryption
Java 密钥生成和解密
Cipher cipher = Cipher.getInstance("RSA");
KeyFactory fact = KeyFactory.getInstance("RSA");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024); // 1024 used for normal
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
FileOutputStream fos = null;
ObjectOutputStream oos = null;
Code to store the private key in file which is used to decrypt in decrypt method.
将私钥存储在文件中的代码,用于在解密方法中解密。
RSAPrivateKeySpec rsaPrivKeySpec = fact.getKeySpec(privateKey,
RSAPrivateKeySpec.class);
System.out.println("Writing private key...");
fos = new FileOutputStream(PRIVATE_KEY_FILE);
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos.writeObject(rsaPrivKeySpec.getModulus());
oos.writeObject(rsaPrivKeySpec.getPrivateExponent());
oos.close();
Decrypt method
解密方法
public String decrypt(String ciphertext)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException
{
if (ciphertext.length() == 0) return null;
byte[] dec = org.apache.commons.codec.binary.Base64.decodeBase64(ciphertext);
try {
System.out.println("Private Key file name----"+PRIVATE_KEY_FILE);
privateKey = readPrivateKeyFromFile(PRIVATE_KEY_FILE);
} catch (IOException e) {
e.printStackTrace();
}
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(dec);
return new String(decrypted, PLAIN_TEXT_ENCODING);
}
//reading private key from file
public PrivateKey readPrivateKeyFromFile(String fileName)
throws IOException {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(new File(fileName));
ois = new ObjectInputStream(fis);
System.out.println("Private Key file-"+fileName);
BigInteger modulus = (BigInteger) ois.readObject();
BigInteger exponent = (BigInteger) ois.readObject();
// Get Private Key
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privateKey = fact.generatePrivate(rsaPrivateKeySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ois != null) {
ois.close();
if (fis != null) {
fis.close();
}
}
}
return null;
}
采纳答案by user2498258
Thanks Oleg for the detailed information. I will definitely take a look into it.
感谢 Oleg 提供详细信息。我一定会研究一下。
For now I switched to jsencrypt and it seems to work fine.
现在我切换到 jsencrypt,它似乎工作正常。
https://github.com/travist/jsencrypt
https://github.com/travist/jsencrypt
EDIT
编辑
How you get the encoded public key for the js encrypt?
你如何获得 js 加密的编码公钥?
回答by Oleg Estekhin
From the Cryptico
documentation it seems that it is not a simple RSA encryption, but a complex operation that generates AES key, encrypts it with RSA, encrypts the data with AES and outputs a concatenation of encrypted AES key and encrypted data. If you want to decrypt that in Java you will have to check the Cryptico
source code and reimplement the same in Java.
从Cryptico
文档看来,它不是一个简单的 RSA 加密,而是一个复杂的操作,它生成 AES 密钥,用 RSA 加密它,用 AES 加密数据并输出加密的 AES 密钥和加密数据的串联。如果你想在 Java 中解密它,你必须检查Cryptico
源代码并在 Java 中重新实现它。
As for your current attempt and javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
error:
至于您当前的尝试和javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
错误:
When you do not specify the full transformation the default JCE transformation for RSA is RSA/ECB/PKCS1Padding.
当您未指定完整转换时,RSA 的默认 JCE 转换为RSA/ECB/PKCS1Padding。
In this mode the RSA encrypts or decrypts a single block of data which the length is not greater than the size of the key (more specifically, if the input sequence of bytes is interpreted as a big integer, its value should be less that the modulus used by the RSA). You can find additional information in thisand thisquestions.
在这种模式下,RSA 加密或解密长度不大于密钥大小的单个数据块(更具体地说,如果将输入的字节序列解释为一个大整数,则其值应小于模数RSA 使用)。你可以找到更多的信息,这个和这个问题。
With the key size of 1024 bits the maximum data size is 128 bytes, and that is exactly what the exception says because the output of Cryptico
is obviously not a single RSA block and its length is greater that expected by "plain" RSA. Trying to use some other cipher mode or padding mode in Java will not help in that situation either.
密钥大小为 1024 位,最大数据大小为 128 字节,这正是异常所说的,因为 的输出Cryptico
显然不是单个 RSA 块,其长度大于“普通”RSA 的预期长度。在这种情况下,尝试在 Java 中使用其他密码模式或填充模式也无济于事。
回答by Srinivasan Thirupathi
Here is the solution for data Encryption from JS and Decrypt on Java(server side). I have used Cryptico js library for encryption(http://wwwtyro.github.io/cryptico/).
这是从 JS 加密数据和在 Java(服务器端)解密的解决方案。我使用 Cryptico js 库进行加密(http://wwwtyro.github.io/cryptico/)。
First of all we have to generate the java Keystore file from your local system. Don't use other Keystore files such as online Keystore. For creating the java Keystore(JKS) you can use KeyStore Explorertool.
首先,我们必须从本地系统生成 java Keystore 文件。不要使用其他 Keystore 文件,例如在线 Keystore。要创建 java Keystore(JKS),您可以使用KeyStore Explorer工具。
Below is the config I have used, using KeyStore Explorertool
下面是我使用的配置,使用KeyStore Explorer工具
- Keystore type - JKS
- RSA algorithm - Keysize 1024
- Version - version 3
- Signature algorithm - SHA256 with RSA
- Validity period - 99 years(based on your requirement)
- Name filed - Fill all the mandatory fields - remember the "alias" and "password" what you entered here.
- 密钥库类型 - JKS
- RSA 算法 - 密钥大小 1024
- 版本 - 版本 3
- 签名算法 - 带有 RSA 的 SHA256
- 有效期 - 99 年(根据您的要求)
- 名称归档 - 填写所有必填字段 - 记住您在此处输入的“别名”和“密码”。
Finally, save the file as .jks on your local system.
最后,在本地系统上将文件另存为 .jks。
Step-1
第1步
we have to use this Keystore file on the java side and we send the public key to the frontend.
我们必须在 java 端使用这个 Keystore 文件,然后将公钥发送到前端。
I have created the service class which is responsible to load Keystore from the keystore file path(string), Keypair and Decrypt. You have to provide the alias, password, keystore type.
我创建了负责从密钥库文件路径(字符串)、密钥对和解密加载密钥库的服务类。您必须提供别名、密码、密钥库类型。
public KeyPair getExistingKeyStoreKeyPair(String keystorePath){
KeyPair generateKeyPair = null
try {
File file = new File(keystorePath)
KeyStore keyStore = loadKeyStore(file, "password", "JKS")
generateKeyPair = getKeyPair(keyStore, "fin360", "password")
} catch (Exception ex){
println(ex)
}
return generateKeyPair
}
public KeyStore loadKeyStore(final File keystoreFile, final String password, final String keyStoreType) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null")
}
final URI keystoreUri = keystoreFile.toURI()
final URL keystoreUrl = keystoreUri.toURL()
final KeyStore keystore = KeyStore.getInstance(keyStoreType)
InputStream is = null
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray())
} finally {
if (null != is) {
is.close()
}
}
return keystore;
}
public KeyPair getKeyPair(final KeyStore keystore, final String alias, final String password) {
PublicKey publicKey
PrivateKey privateKey
Key key
KeyPair keyPair
try {
key = (PrivateKey) keystore.getKey(alias, password.toCharArray())
final Certificate cert = keystore.getCertificate(alias)
publicKey = cert.getPublicKey()
privateKey = key
keyPair = new KeyPair(publicKey, privateKey)
} catch (Exception ex){
println(ex)
}
return keyPair;
}
public decryptData(String data, String keystorePath) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException{
try {
byte[] dectyptedText = new byte[1]
byte[] byteArray = new byte[256]
BigInteger passwordInt = new BigInteger(data, 16)
if (passwordInt.toByteArray().length > 256) {
for (int i=1; i<257; i++) {
byteArray[i-1] = passwordInt.toByteArray()[i]
}
} else {
byteArray = passwordInt.toByteArray();
}
KeyPair generateKeyPair = getExistingKeyStoreKeyPair(keystorePath)
PrivateKey privateKey = generateKeyPair.getPrivate()
Cipher cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
dectyptedText = cipher.doFinal(byteArray)
String txt2 = new String(dectyptedText)
return txt2
}
catch (Exception ex){
println(ex)
return null
}
}
decryptData() method will playing the main role here. When you send the value data.getBytes() directly to the dycrypt method cipher.doFinal(byteArray)
you get the exception - IllegalBlockSizeException size should not more than 128 bytes. So we have get rid of the issue I get the workaroud here - [Getting 1 byte extra in the modulus RSA Key and sometimes for exponents alsoBasically it adds the zero when we converting data from BigInteger to byteArray. So I removed the zero from the array.
在这里,decryptData() 方法将发挥主要作用。当您将值 data.getBytes() 直接发送到 dycrypt 方法时,cipher.doFinal(byteArray)
您会收到异常 - IllegalBlockSizeException 大小不应超过 128 字节。所以我们已经摆脱了我在这里得到解决的问题 - [在模数 RSA 密钥中额外获得 1 个字节,有时对于指数也是如此基本上,当我们将数据从 BigInteger 转换为 byteArray 时,它会添加零。所以我从数组中删除了零。
Let's start use the service class to get the key values.
让我们开始使用服务类来获取键值。
String publicKey= null
String keystorePath = your file path
KeyPair generateKeyPair = encryptDecryptService.getExistingKeyStoreKeyPair(keystorePath)
PublicKey publicKey1 = generateKeyPair.getPublic()
KeyFactory keyFactory;
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(BigInteger.ZERO, BigInteger.ZERO)
try {
keyFactory = KeyFactory.getInstance("RSA")
rsaPublicKeySpec = keyFactory.getKeySpec(publicKey1, RSAPublicKeySpec.class)
} catch(NoSuchAlgorithmException e1) {
println(e1)
} catch(InvalidKeySpecException e) {
println(e)
}
String testPublicKey = rsaPublicKeySpec.getModulus().toString(16)
publicKey = testPublicKey
Send you publicKey to JS. In your HTML or servlet import all the required js and jar files(you will get it from the cryptico js library).
将您的公钥发送给 JS。在您的 HTML 或 servlet 中导入所有必需的 js 和 jar 文件(您将从 cryptico js 库中获取它)。
try{
var rsa = new RSAKey();
rsa.setPublic(pub, "10001");
password = rsa.encrypt(password);
formdata = "password="+password+"&dataEncrypt=true";
}
catch (error){
console.log(error);
}
above I have directly used new RSA()
instance(in cryptico library it will be different. Internaly library is using the same) and set the publickey to the instance. We have to use hex string value is '10001'. Form the query string with encrypted data which we send to server. Form data holds the encrypted data well as well as 'dataEncrypt' key value. I used to check whether data is encrypted or not.
上面我直接使用了new RSA()
实例(在cryptico库中它会有所不同。内部库使用的是相同的)并将公钥设置为实例。我们必须使用十六进制字符串值是'10001'。使用我们发送到服务器的加密数据形成查询字符串。表单数据很好地保存了加密数据以及“dataEncrypt”密钥值。我曾经检查数据是否加密。
Finally on the server side you will get the request params and below is the code for decrypt.
最后在服务器端,您将获得请求参数,下面是解密代码。
Boolean isDataEncrypted = false
String decryptedPassword = null
isDataEncrypted = params.containsKey("dataEncrypt")
if(params.containsKey("password")){
if(isDataEncrypted) {
String keystorePath = helperService.fetchKeystoreFilePath()
decryptedPassword = encryptDecryptService.decryptData(params.password, keystorePath)
// update decrypted data into request params
params.password = decryptedPassword
}
}
println("Data decrypted => " + decryptedPassword)