我尝试在 WebCrypto API(客户端)中使用 RSA-OAEP-SHA-512 加密字符串,然后我想使用 Java(服务器端)再次解密该字符串。但是,服务器端解密失败并出现错误:解密中填充错误。
我正在使用 JavaScript 生成 RSA 密钥:
async function generateRSAKeyPair() {
const rsaKeyPair = await window.crypto.subtle.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
hash: { name: 'SHA-512' },
},
true,
['encrypt', 'decrypt']
);
return rsaKeyPair;
}
我正在使用以下方法加密 JSON 字符串:
sync function encryptWithRSA(jsonString, jsonRSAPublicKey) {
// Parse JSON keys
const rsaPublicKey = JSON.parse(jsonRSAPublicKey);
// Import RSA public key
const importedRSAPublicKey = await window.crypto.subtle.importKey(
'jwk',
rsaPublicKey,
{ name: 'RSA-OAEP', hash: { name: 'SHA-512' } },
true,
['encrypt']
);
// Convert JSON String to ArrayBuffer
const aesKeyBuffer = new TextEncoder().encode(jsonString);
// Encrypt AES key using RSA public key
const encryptedAesKeyBuffer = await window.crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
importedRSAPublicKey,
aesKeyBuffer
);
// Convert encrypted AES key to base64
const encryptedAesKeyBase64 = window.btoa(String.fromCharCode(...new Uint8Array(encryptedAesKeyBuffer)));
return encryptedAesKeyBase64;
}
我正在尝试使用 Java 来解密它:
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
public void decrypt() {
try {
PrivateKey privateKey = loadPrivateKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
this.clearText = new String(decryptedBytes);
System.out.println("Decrypted clear text: " + clearText);
} catch (Exception e) {
e.printStackTrace();
System.err.println("Decryption failed: " + e.getMessage());
}
}
private PrivateKey loadPrivateKey() throws Exception {
// Parse the JWK JSON string
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> jwk = mapper.readValue(rsaKey, Map.class);
System.out.println("JWK parsed: " + jwk);
// Extract the key components from the JWK
String nBase64 = (String) jwk.get("n");
String dBase64 = (String) jwk.get("d");
String pBase64 = (String) jwk.get("p");
String qBase64 = (String) jwk.get("q");
String dpBase64 = (String) jwk.get("dp");
String dqBase64 = (String) jwk.get("dq");
String qiBase64 = (String) jwk.get("qi");
String eBase64 = (String) jwk.get("e");
// Decode the Base64 URL-encoded components
BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(nBase64));
BigInteger privateExponent = new BigInteger(1, Base64.getUrlDecoder().decode(dBase64));
BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(eBase64));
BigInteger primeP = new BigInteger(1, Base64.getUrlDecoder().decode(pBase64));
BigInteger primeQ = new BigInteger(1, Base64.getUrlDecoder().decode(qBase64));
BigInteger primeExponentP = new BigInteger(1, Base64.getUrlDecoder().decode(dpBase64));
BigInteger primeExponentQ = new BigInteger(1, Base64.getUrlDecoder().decode(dqBase64));
BigInteger crtCoefficient = new BigInteger(1, Base64.getUrlDecoder().decode(qiBase64));
// Create the RSA private key specification
RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
// Generate the private key
KeyFactory keyFactory = KeyFactory.getInstance(AppConfig.CRYPTO_RSA);
return keyFactory.generatePrivate(keySpec);
}
这给出了以下输出:
javax.crypto.BadPaddingException: Padding error in decryption
at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:383)
at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:419)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2244)
at zkgitclientcli.crypto.RsaEncryptionHandler2.decrypt(RsaEncryptionHandler2.java:48)
at zkgitclientcli.commands.login.DecryptAesKey.execute(DecryptAesKey.java:23)
at zkgitclientcli.commands.CommandManager.executeCommand(CommandManager.java:44)
at zkgitclientcli.ZkGit.main(ZkGit.java:12)
at org.codehaus.mojo.exec.ExecJavaMojo.doMain(ExecJavaMojo.java:385)
at org.codehaus.mojo.exec.ExecJavaMojo.doExec(ExecJavaMojo.java:374)
at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0(ExecJavaMojo.java:296)
at java.base/java.lang.Thread.run(Thread.java:1583)
Decryption failed: Padding error in decryption
私钥导入很可能有效,因为字符串使用相应的公钥在 Java 中加密,并使用此方法成功解密。仅当使用 WebCrypto 加密字符串作为输入时才会失败。
另外,我可以编写一个JavaScript方法来成功解密加密字符串,因此base64编码的加密字符串是可以解密的,但在Java中则不行...
我尝试了以下设置,但没有成功: SHA-1:RSA/ECB/OAEPWithSHA-1AndMGF1Padding SHA-224:RSA/ECB/OAEP 具有 SHA-224 和 MGF1 填充 SHA-256:RSA/ECB/OAEP 具有 SHA-256 和 MGF1 填充 SHA-384:带有 SHA-384 和 MGF1 填充的 RSA/ECB/OAEP
这是基于上面提供的评论的工作代码:
public void decrypt() {
try {
PrivateKey privateKey = loadPrivateKey();
// Create OAEPParameterSpec
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-512",
"MGF1",
MGF1ParameterSpec.SHA512,
PSource.PSpecified.DEFAULT
);
Cipher cipher = Cipher.getInstance(AppConfig.CRYPTO_RSA_OAEP);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
clearText = new String(decryptedBytes);
} catch (Exception e) {
clearText = e.getMessage();
}
}