RSA加密Javascript和解密Java



使用不同的组合将近2天。我正在使用RSA算法在java中生成一个非对称密钥对(公钥和私钥),并尝试在服务器端使用javascript中的公钥来加密一些文本并在java中解密。在尝试解密回用javascript加密的字符串时,我遇到了"javax.crypto.IollegalBlockSizeException:数据不得长于128字节"异常。非常感谢您的帮助。。。

使用thi Javascript库进行加密

https://github.com/wwwtyro/cryptico

var publicKeyString="//base64在java 中生成的编码公钥字符串

这是我的javascript代码

var EncryptionResult = cryptico.encrypt("somestring", publicKeyString);
console.log("Encrypted status-"+EncryptionResult.status);
console.log("Encrypted String-"+EncryptionResult.cipher);

它正在成功加密字符串。

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;

将私钥存储在文件中的代码,该文件用于在解密方法中进行解密。

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();

解密方法

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;
}    

Cryptico文档来看,它似乎不是一个简单的RSA加密,而是一个复杂的操作,它生成AES密钥,用RSA加密,用AES加密数据,并输出加密的AES密钥和加密数据的级联。如果你想在Java中解密它,你必须检查Cryptico源代码,并在Java中重新实现它。

至于您当前的尝试和javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes错误:

如果未指定完整转换,RSA的默认JCE转换为RSA/ECB/PKCS1Padding。

在这种模式下,RSA加密或解密长度不大于密钥大小的单个数据块(更具体地说,如果输入的字节序列被解释为大整数,则其值应该小于RSA使用的模数)。您可以在本问题和本问题中找到更多信息。

密钥大小为1024位时,最大数据大小为128字节,这正是异常所说的,因为Cryptico的输出显然不是单个RSA块,其长度大于"普通"RSA所期望的长度。在这种情况下,尝试使用Java中的其他加密模式或填充模式也无济于事。

感谢Oleg提供的详细信息。我一定会调查一下。

现在我切换到jsencrypt,它似乎工作得很好。

https://github.com/travist/jsencrypt

编辑

如何获得js加密的编码公钥?

以下是Java(服务器端)上JS和Decrypt的数据加密解决方案。我已经使用Cryptico js库进行加密(http://wwwtyro.github.io/cryptico/)。

首先,我们必须从您的本地系统生成javaKeystore文件。不要使用其他Keystore文件,如在线Keystore。为了创建java密钥库(JKS),您可以使用KeyStoreExplorer工具。

下面是我使用KeyStore Explorer工具使用的配置

  1. 密钥库类型-JKS
  2. RSA算法-密钥大小1024
  3. 版本-版本3
  4. 签名算法-SHA256与RSA
  5. 有效期-99年(根据您的要求)
  6. 名称字段-填写所有必填字段-记住您在此处输入的"别名"one_answers"密码">

最后,将文件保存为本地系统上的.jks。

第1步

我们必须在java端使用这个Keystore文件,并将公钥发送到前端。

我已经创建了服务类,负责从密钥库文件路径(字符串)、Keypair和Decrypt加载密钥库。您必须提供别名、密码和密钥存储类型。

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()方法将在这里扮演主要角色。当您将值data.getBytes()直接发送到dycrypt方法cipher.doFinal(byteArray)时,您会得到异常-IlegalBlockSizeException大小不应超过128字节。所以我们已经解决了这个问题,我在这里得到了解决方法——[在模数RSA密钥中获得1个字节的额外值,有时也用于指数基本上,当我们将数据从BigInteger转换为byteArray时,它会添加零。所以我从数组中删除了零。

让我们开始使用服务类来获取键值。

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

将publicKey发送给JS。在HTML或servlet中,导入所有必需的js和jar文件(您将从crypticojs库中获得)。

try{
var rsa = new RSAKey();
rsa.setPublic(pub, "10001");
password = rsa.encrypt(password);
formdata = "password="+password+"&dataEncrypt=true";
}
catch (error){
console.log(error);
}

上面我直接使用了new RSA()实例(在cryptico库中会有所不同。Internally库使用相同),并为该实例设置了公钥。我们必须使用十六进制字符串值为"10001"。使用我们发送到服务器的加密数据形成查询字符串。表单数据保存加密的数据以及"dataEncrypt"键值。我过去常常检查数据是否加密。

最后,在服务器端,您将获得请求参数,下面是用于解密的代码。

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)

最新更新