Java 谁能告诉我为什么我能';在这个AES实现中不使用填充吗?
我正在尝试编写一个应用程序,用户应该能够从算法操作模式(CBC、CTR…)中进行选择 我使用这段代码(不是我写的)进行AES加密,但我注意到我只能使用NoPadding,我不明白为什么Java 谁能告诉我为什么我能';在这个AES实现中不使用填充吗?,java,encryption,aes,padding,Java,Encryption,Aes,Padding,我正在尝试编写一个应用程序,用户应该能够从算法操作模式(CBC、CTR…)中进行选择 我使用这段代码(不是我写的)进行AES加密,但我注意到我只能使用NoPadding,我不明白为什么 public void setPassword(String password) throws UnsupportedEncodingException { this.password = password.getBytes("UTF-16LE"); debug("Using password:
public void setPassword(String password) throws UnsupportedEncodingException {
this.password = password.getBytes("UTF-16LE");
debug("Using password: ", this.password);
}
public void encrypt(int version, String fromPath, String toPath) throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
encrypt(version, in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
public void encrypt(int version, InputStream in, OutputStream out) throws IOException, GeneralSecurityException {
try {
byte[] text = null;
ivSpec1 = new IvParameterSpec(generateIv1());
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
ivSpec2 = new IvParameterSpec(generateIV2());
aesKey2 = new SecretKeySpec(generateAESKey2(), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
out.write("AES".getBytes("UTF-8")); // Heading.
out.write(version); // Version.
out.write(0); // Reserved.
if (version == 2) { // No extensions.
out.write(0);
out.write(0);
}
out.write(ivSpec1.getIV()); // Initialization Vector.
text = new byte[BLOCK_SIZE + KEY_SIZE];
cipher.init(Cipher.ENCRYPT_MODE, aesKey1, ivSpec1);
cipher.update(ivSpec2.getIV(), 0, BLOCK_SIZE, text);
cipher.doFinal(aesKey2.getEncoded(), 0, KEY_SIZE, text, BLOCK_SIZE);
out.write(text); // Crypted IV and key.
debug("IV2 + AES2 ciphertext: ", text);
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
text = hmac.doFinal(text);
out.write(text); // HMAC from previous cyphertext.
debug("HMAC1: ", text);
cipher.init(Cipher.ENCRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
text = new byte[BLOCK_SIZE];
int len, last = 0;
while ((len = in.read(text)) > 0) {
cipher.update(text, 0, BLOCK_SIZE, text);
hmac.update(text);
out.write(text); // Crypted file data block.
last = len;
}
last &= 0x0f;
out.write(last); // Last block size mod 16.
debug("Last block size mod 16: " + last);
text = hmac.doFinal();
out.write(text); // HMAC from previous cyphertext.
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
public void decrypt(String fromPath, String toPath) throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
decrypt(new File(fromPath).length(), in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
public void decrypt(long inSize, InputStream in, OutputStream out) throws IOException, GeneralSecurityException {
try {
byte[] text = null, backup = null;
long total = 3 + 1 + 1 + BLOCK_SIZE + BLOCK_SIZE + KEY_SIZE + SHA_SIZE + 1 + SHA_SIZE;
int version;
text = new byte[3];
readBytes(in, text); // Heading.
if (!new String(text, "UTF-8").equals("AES")) {
throw new IOException("Invalid file header");
}
version = in.read(); // Version.
if (version < 1 || version > 2) {
throw new IOException("Unsupported version number: " + version);
}
debug("Version: " + version);
in.read(); // Reserved.
if (version == 2) { // Extensions.
text = new byte[2];
int len;
do {
readBytes(in, text);
len = ((0xff & (int) text[0]) << 8) | (0xff & (int) text[1]);
if (in.skip(len) != len) {
throw new IOException("Unexpected end of extension");
}
total += 2 + len;
debug("Skipped extension sized: " + len);
} while (len != 0);
}
text = new byte[BLOCK_SIZE];
readBytes(in, text); // Initialization Vector.
ivSpec1 = new IvParameterSpec(text);
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
cipher.init(Cipher.DECRYPT_MODE, aesKey1, ivSpec1);
backup = new byte[BLOCK_SIZE + KEY_SIZE];
readBytes(in, backup); // IV and key to decrypt file contents.
debug("IV2 + AES2 ciphertext: ", backup);
text = cipher.doFinal(backup);
ivSpec2 = new IvParameterSpec(text, 0, BLOCK_SIZE);
aesKey2 = new SecretKeySpec(text, BLOCK_SIZE, KEY_SIZE, CRYPT_ALG);
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
backup = hmac.doFinal(backup);
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC1: ", text);
total = inSize - total; // Payload size.
if (total % BLOCK_SIZE != 0) {
throw new IOException("Input file is corrupt");
}
if (total == 0) { // Hack: empty files won't enter block-processing
// for-loop below.
in.read(); // Skip last block size mod 16.
}
debug("Payload size: " + total);
cipher.init(Cipher.DECRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
backup = new byte[BLOCK_SIZE];
text = new byte[BLOCK_SIZE];
for (int block = (int) (total / BLOCK_SIZE); block > 0; block--) {
int len = BLOCK_SIZE;
if (in.read(backup, 0, len) != len) { // Cyphertext block.
throw new IOException("Unexpected end of file contents");
}
cipher.update(backup, 0, len, text);
hmac.update(backup, 0, len);
if (block == 1) {
int last = in.read(); // Last block size mod 16.
debug("Last block size mod 16: " + last);
len = (last > 0 ? last : BLOCK_SIZE);
}
out.write(text, 0, len);
}
out.write(cipher.doFinal());
backup = hmac.doFinal();
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
您将获得该异常,因为正如消息所述,输出缓冲区太短 我不知道引发异常的是哪一行(您没有发布堆栈跟踪),但代码似乎只处理特定长度的密文,并且在应用填充时失败(因为PKCS#5填充可以添加整个块) 加密“主”加密密钥有什么意义?它没有增加任何安全性,但更复杂。正如那句古老的格言,复杂性是安全的敌人
同时也要注意家用(或从互联网上粘贴的副本)加密的危险。我可以立即发现至少一个漏洞–MAC-then-pad序列允许填充oracle攻击。您最好使用经过测试的解决方案,例如。您会遇到这种异常,因为正如消息所说,输出缓冲区太短 我不知道引发异常的是哪一行(您没有发布堆栈跟踪),但代码似乎只处理特定长度的密文,并且在应用填充时失败(因为PKCS#5填充可以添加整个块) 加密“主”加密密钥有什么意义?它没有增加任何安全性,但更复杂。正如那句古老的格言,复杂性是安全的敌人
同时也要注意家用(或从互联网上粘贴的副本)加密的危险。我可以立即发现至少一个漏洞–MAC-then-pad序列允许填充oracle攻击。您最好使用经过测试的解决方案,例如。这是一大堆代码。请您将其简化为再现您的问题所需的最少代码。哦,你能告诉我们当你尝试使用填充时会发生什么吗?不要允许ECB模式,这是不安全的。用最少的选项将应用程序简化为CBC模式,并使其正常工作。只有当最小应用程序正常工作时,您才应该添加更多选项。@rossum我不使用ECB,它甚至不工作。我第一次写问题时犯了一个错误。@Duncan Jones我忘了写我得到的错误。那是一大堆代码。请您将其简化为再现您的问题所需的最少代码。哦,你能告诉我们当你尝试使用填充时会发生什么吗?不要允许ECB模式,这是不安全的。用最少的选项将应用程序简化为CBC模式,并使其正常工作。只有当最小应用程序正常工作时,您才应该添加更多选项。@rossum我不使用ECB,它甚至不工作。我第一次写问题时犯了一个错误。@Duncan Jones我忘了写我得到的错误。谢谢!我自己不太懂怎么写,而且时间紧迫。我写了界面,它做了我需要的,但我很好奇为什么我不能使用填充。你的解释似乎很好。你知道像keychar这样也有DES-ecryption的东西吗?你从明文开始,然后添加填充。您加密的是纯文本+填充。因此,您的输出缓冲区需要有足够的空间来容纳这两者。因为总是添加填充,所以加密输出的长度总是比最初的明文长。@StoicaDan如果Keyczar支持DES,我会非常非常惊讶。DES密钥非常弱,在现代家用计算机上几小时内就可以强制使用。@ntoskrnl我知道DES很弱,但我想做的是一个应用程序,允许用户选择他们想要使用的加密算法。@StoicaDan这有什么意义?依靠一个已知的工作算法(即AES)是一个更好的主意,尤其是考虑到大多数用户对密码术一无所知,无法自己选择一个合理的组合。谢谢!我自己不太懂怎么写,而且时间紧迫。我写了界面,它做了我需要的,但我很好奇为什么我不能使用填充。你的解释似乎很好。你知道像keychar这样也有DES-ecryption的东西吗?你从明文开始,然后添加填充。您加密的是纯文本+填充。因此,您的输出缓冲区需要有足够的空间来容纳这两者。因为总是添加填充,所以加密输出的长度总是比最初的明文长。@StoicaDan如果Keyczar支持DES,我会非常非常惊讶。DES密钥非常弱,在现代家用计算机上几小时内就可以强制使用。@ntoskrnl我知道DES很弱,但我想做的是一个应用程序,允许用户选择他们想要使用的加密算法。@StoicaDan这有什么意义?依靠一个已知的工作算法(即AES)是一个更好的主意,特别是考虑到大多数用户对密码术一无所知,无法自行选择合理的组合这一事实。
Output buffer too short: 32 bytes given, 48 bytes needed
javax.crypto.ShortBufferException: Output buffer too short: 32 bytes given, 48 bytes needed