使用不同的组合将近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工具使用的配置
- 密钥库类型-JKS
- RSA算法-密钥大小1024
- 版本-版本3
- 签名算法-SHA256与RSA
- 有效期-99年(根据您的要求)
- 名称字段-填写所有必填字段-记住您在此处输入的"别名"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)