我有解密的问题;我收到以下错误:
javax.crypto.BadPaddingException:解密错误
我的加密代码如下:
userName = URLDecoder.decode(userName, "ISO-8859-1");
Cipher objCipherTunkicloud = Cipher.getInstance("RSA/ECB/PKCS1Padding");
objCipherTunkicloud.init(Cipher.ENCRYPT_MODE, loadPublicKey("/keylecordonbleu/public.key", "RSA"));
byte[] arrDecryptedKeyBytes = objCipherTunkicloud.doFinal(userName.getBytes(StandardCharsets.UTF_8));
log.error("SECURITY - key en array de bytes");
String tkn = new String(arrDecryptedKeyBytes);
userName = URLEncoder.encode(tkn, "ISO-8859-1");
并解密它是这样的:
userName = URLDecoder.decode(userName, "ISO-8859-1");
Cipher objCipherTunkicloud = Cipher.getInstance("RSA/ECB/PKCS1Padding");
objCipherTunkicloud.init(Cipher.DECRYPT_MODE, loadPrivateKey("/keylecordonbleu/private.key", "RSA"));
byte[] arrDecryptedKeyBytes = objCipherTunkicloud.doFinal(userName.getBytes(StandardCharsets.ISO_8859_1));
String tkn = new String(arrDecryptedKeyBytes);
问题是什么,我该如何解决?
解密时,此行中出现错误:
byte[] arrDecryptedKeyBytes = objCipherTunkicloud.doFinal(userName.getBytes(StandardCharsets.ISO_8859_1));
答案 0 :(得分:2)
@JB Nizet的评论解释并解决了您的问题。我稍后会详细说明一下:
String(byte[])
将给定的字节数组解释为默认(或给定)编码中的字符数据。String(byte[] encrypted).getBytes()
不会为所有可能的字节数组返回encrypted
。java.util.Base64
)通常用于打印加密结果。Cipher
会处理此问题并根据需要填写您的输入内容。BadPaddingException
正如@ dave_thompson_085指出的那样,如果强制java使用String(encrypted, StandardCharsets.ISO_8859_1)
和{{1强制java将字节数组解释为ISO-8859-1(latin1),则可以将字节数组转换为字符串并返回而不会丢失}}。 ISO-8859-1映射所有256字节值,并且没有任何"无效值"。我在代码中添加了相应的编码器/解码器。
但是:确保您在走这条路线时知道后果:
encrypted.getBytes(StandardCharsets.ISO_8859_1)
可以标记字符串的结尾,0x00
和0x0a
可以被操纵,控制字符被解释。你的代码做了很多不同的事情。特别是对于加密技术,它通常需要支付单独关注和独立测试。为了重现并解决问题,我做了一些改变:
0x0d
/ URLDecode
,因为它没有导致问题(可能属于另一个图层......)。URLEncode
方法和您的密钥文件...我已将其替换为每次测试时生成的loadPublicKey("/keylecordonbleu/public.key", "RSA")
。您可能希望从此开始,并在其他代码正常工作后添加密钥。KeyPair
编码为byte[]
,并将String
解码为String
进行解密。这允许你:
byte[]
,无需编码为String。这在您的代码中已经有效。encryptDecryptRoundtrip(String userName)
)显然不适用于所有输入并验证同样适用于Base64编码(testSimpleEncoder()
)。使用您的解密/加密代码进行分类:
testBase64Encoder()
测试类:
public class CryptoUtils {
private static final String ALGORITHM = "RSA";
private static final String TRANSFORMATION = ALGORITHM + "/ECB/PKCS1Padding";
public interface Encoder extends Function<byte[], String> { };
public interface Decoder extends Function<String, byte[]> { };
public static class EncoderNotWorking implements Encoder {
@Override
public String apply(byte[] encrypted) {
return new String(encrypted);
}
}
public static class DecoderNotWorking implements Decoder {
@Override
public byte[] apply(String encrypted) {
return encrypted.getBytes();
}
}
public static class EncoderLatin1 implements Encoder {
@Override
public String apply(byte[] encrypted) {
return new String(encrypted, StandardCharsets.ISO_8859_1);
}
}
public static class DecoderLatin1 implements Decoder {
@Override
public byte[] apply(String encrypted) {
return encrypted.getBytes(StandardCharsets.ISO_8859_1);
}
}
public static class EncoderBase64 implements Encoder {
@Override
public String apply(byte[] encrypted) {
return new String(Base64.getEncoder().encode(encrypted));
}
}
public static class DecoderBase64 implements Decoder {
@Override
public byte[] apply(String encrypted) {
return Base64.getDecoder().decode(encrypted);
}
}
/** Return Cipher for the given mode (de/encrypt) and key. */
public Cipher getInitCipher(int opmode, Key key) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(opmode, key);
return cipher;
}
/** Generate a key pair for testing. */
public KeyPair generateKeyPair()
throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
return keyGen.generateKeyPair();
}
public byte[] encrypt(Key publicKey, String userName)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
byte[] toEncrypt = userName.getBytes();
Cipher cipher = getInitCipher(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(toEncrypt);
}
public String decrypt(Key privateKey, byte[] encryptedUserName)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = getInitCipher(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(encryptedUserName);
return new String(decrypted);
}
/** Encrypt and encode using the given Encoder, */
public String encryptAndEncode(Key publicKey, String userName,
Encoder encoder) throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
byte[] encrypted = encrypt(publicKey, userName);
return encoder.apply(encrypted);
}
/** Decrypt and Decode using the given Decoder, */
public String decodeAndDecrypt(Key privateKey, String encryptedUserName,
Decoder decoder) throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
byte[] toDecrypt = decoder.apply(encryptedUserName);
return decrypt(privateKey, toDecrypt);
}
/** Encrypt and decrypt the given String, and assert the result is correct. */
public void encryptDecryptRoundtrip(String userName)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
NoSuchProviderException {
CryptoUtils crypto = new CryptoUtils();
KeyPair keys = crypto.generateKeyPair();
byte[] encrypted = crypto.encrypt(keys.getPublic(), userName);
String decrypted = crypto.decrypt(keys.getPrivate(), encrypted);
assert decrypted.equals(userName);
}
/**
* As @link {@link #encryptDecryptRoundtrip(String)}, but further encodes and
* decodes the result of the encryption to/from a String using the given
* Encoder/Decoder before decrypting it.
*/
public void encodeDecodeRoundtrip(Encoder encoder, Decoder decoder,
String userName) throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
NoSuchProviderException {
CryptoUtils crypto = new CryptoUtils();
KeyPair keys = crypto.generateKeyPair();
String encrypted = crypto.encryptAndEncode(keys.getPublic(), userName,
encoder);
// encrypted could now be stored and later loaded...
String decrypted = crypto.decodeAndDecrypt(keys.getPrivate(), encrypted,
decoder);
assert decrypted.equals(userName);
}
/** Test the working examples*/
public static void main(String[] args) throws NoSuchAlgorithmException,
NoSuchProviderException, InvalidKeyException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException {
CryptoUtils crypto = new CryptoUtils();
String userName = "John Doe";
crypto.encryptDecryptRoundtrip(userName);
crypto.encodeDecodeRoundtrip(new EncoderBase64(), new DecoderBase64(),
userName);
}
}
答案 1 :(得分:1)
看起来问题是您使用doFinal(userName.getBytes(StandardCharsets.UTF_8));
加密并使用doFinal(userName.getBytes(StandardCharsets.ISO_8859_1));
进行解密
答案 2 :(得分:0)
您对密文的URL编码的使用是不寻常的,低效的,并且通常可能不安全,但如果您正确创建它们,则由Java实现的URL编码可以保留8859-1 的所有256个字符 。如果提供了第四个参数,则代码的以下压缩提取物会起作用,导致new String(encryptedbytes)
指定8859-1而不是默认为JVM的默认字符集,默认字符集因平台和环境而异,通常不是8859-1。
static void SO46244541CryptAsURL (String... args) throws Exception {
// arguments: data pubkeyfile(der) prvkeyfile(der) flag(if present specify 8859-1 on conversion)
String clear = args[0];
KeyFactory fact = KeyFactory.getInstance("RSA");
Cipher objCipherTunkicloud = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// encrypt side
objCipherTunkicloud.init(Cipher.ENCRYPT_MODE, fact.generatePublic(new X509EncodedKeySpec(read_file(args[1]))));
byte[] arrDecryptedKeyBytes = objCipherTunkicloud.doFinal(clear.getBytes(StandardCharsets.UTF_8));
// for correct result must enable flag and specify 8859-1 on ctor
String tkn = args.length>3? new String(arrDecryptedKeyBytes,StandardCharsets.ISO_8859_1): new String(arrDecryptedKeyBytes);
String output = URLEncoder.encode(tkn, "ISO-8859-1");
System.out.println (output);
// decrypt side
String temp = URLDecoder.decode(output, "ISO-8859-1");
//reused: Cipher objCipherTunkicloud = Cipher.getInstance("RSA/ECB/PKCS1Padding");
objCipherTunkicloud.init(Cipher.DECRYPT_MODE, fact.generatePrivate(new PKCS8EncodedKeySpec(read_file(args[2]))));
arrDecryptedKeyBytes = objCipherTunkicloud.doFinal(temp.getBytes(StandardCharsets.ISO_8859_1));
System.out.println (new String(arrDecryptedKeyBytes));
}
public static byte[] read_file (String filename) throws Exception {
return Files.readAllBytes(new File(filename).toPath());
}