我想知道是否可以仅通过解密方法来重现加密过程?
换句话说,我已经获得了此代码,并且我想返回加密。
public class Encrypter {
private String iv = "qj839.SkW@a#pPsX";
private String SecretKey = "!D&@DKmq81-CClo";
String keyphrase = "SomeWords";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
public Encrypter() {
SecretKey = Hash.getMD5(keyphrase).substring(4, 20);
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
public byte[] decrypt(String code) throws Exception {
if (code == null || code.length() == 0) {
return null;
}
code = code.replaceAll("-", "").toLowerCase();
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
//Remove trailing zeroes
if (decrypted.length > 0) {
int trim = 0;
for (int i = decrypted.length - 1; i >= 0; i--) {
if (decrypted[i] == 0) {
trim++;
}
}
if (trim > 0) {
byte[] newArray = new byte[decrypted.length - trim];
int length = decrypted.length - trim;
int srcPos = 0;
int destPos = 0;
while (length > 0) {
newArray[destPos] = decrypted[srcPos];
srcPos++;
destPos++;
length--;
}
decrypted = newArray;
}
}
} catch (Exception e) {
return null;
}
return decrypted;
}
public String decryptString(String text) throws Exception {
byte[] temp = decrypt(text);
if (temp == null) return null;
return new String(temp);
}
public byte[] hexToBytes(String str) {
if (str == null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
return buffer;
}
}
}
最佳答案
由于这是使用 AES 的对称加密,因此加密器
实际上保持不变。我怀疑您不明白该行后面有附加代码的原因
decrypted = cipher.doFinal(hexToBytes(code));
这是实际的解密。
CBC 模式下的 AES 仅适用于 block 大小的倍数,即 AES 的 16 字节。如果要加密任意长度的明文,则需要对明文进行填充,直到达到预期的长度。通常这是通过 PKCS#5/PKCS#7 填充完成的,但在本例中它是零填充。这意味着填充实际上是 0 到 15 个尾随 0x00 字节。
通过将填充指定为“NoPadding”,开发人员就有了实现填充的任务。
解密代码有点笨拙和多余,但它有效。
代码类似于:
/**
* TODO: add error handling
*/
public String encrypt(byte[] plaintext) {
int bs = cipher.getBlockSize();
int targetSize = plaintext.length + (bs - plaintext.length % bs) % bs;
byte[] plaintextPadded = new byte[targetSize];
System.arraycopy(plaintext, 0, plaintextPadded, 0, plaintext.length);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintextPadded);
return bytesToHex(encrypted);
}
// TODO: add bytesToHex implementation
此代码使用了 new byte[x]
实际上用 0x00 初始化所有字节的事实,因此无需显式写入填充字节。
其他注意事项:
此处使用的零填充只能用于字符串数据或一般不能以 0x00 字节结束的数据。如果要加密任意数据,则需要使用
Cipher.getInstance("AES/CBC/PKCS5Padding")
。切勿使用静态 IV。它应该是在同一 key 下为每次加密随机生成的。由于它不必是 secret 的,因此您可以简单地将其添加到密文中,以便在解密过程中将其切掉。
AES key 应该由任意字节组成。如果将 key 空间限制为可打印字符(
String
),则暴力破解会更容易。 (并不是说一开始就很容易)您使用的
new String(byte[])
和string.getBytes()
不带其他参数。您需要向两者传递一个附加参数,以指定字符编码,例如“UTF-8”。如果不这样做,那么在尝试解密在不同系统上使用不同默认字符集加密的密文时可能会遇到问题。
关于java - 如果我只有解密方法,如何重新创建 AES 加密方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32786457/