用例1(工作基线):
用例一是简单明了的,是实现/工作的。
用例2(未确定合适的解决方案):
问题陈述:
加密和解密"abc“不会产生与分别加密和解密"a”、"b“和"c”相同的结果,因此用例2中描述的“块”文件不会被成功解密。
// e.g.
decrypt(encrypt("abc")) != decrypt(encrypt("a") + encrypt("b") + encrypt("c"))的实际问题:
..。因此,问题是,如何配置一个Java密码流,该流可以一次加密一个块,(a)不事先知道加密块,(b)使用单个输入流密码包装器(不需要知道文件被追加的索引).
发布于 2015-01-31 00:04:33
不幸的是,在这种情况下,你不能吃你的蛋糕和它。
你必须要么
decrypt(encrypt("abc")) == decrypt(encrypt("a") + encrypt("b") + encrypt("c")) (又名琐碎,不推荐)第一条绝对是一个更好的选择,而且比你想象的更容易。详情如下。
第二,您可以使用类似于Vigenere密码的东西,这将允许您一举解密整个文件,但在加密强度方面是一种妥协。
关于1号的详细信息
这样做的方法是,例如,在每个块的开头保留四个字节(32位整数)。这个整数表示块的长度。因此,要解密您将:
n。n字节并解密。n。n字节,解密并附加到第一个解密块。显然,这使得块加密变得容易,因为您所要做的就是首先编写要追加的加密字节数。
发布于 2015-02-04 06:30:41
我找到了一个与我的特定问题足够接近的解决方案(从这个职位窃取),尽管与问题声明略有不同(一个流都没有)。
public static void appendAES(File file, byte[] data, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
RandomAccessFile rfile = new RandomAccessFile(file,"rw");
byte[] iv = new byte[16];
byte[] lastBlock = null;
if (rfile.length() % 16L != 0L) {
throw new IllegalArgumentException("Invalid file length (not a multiple of block size)");
} else if (rfile.length() == 16) {
throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)");
} else if (rfile.length() == 0L) {
// new file: start by appending an IV
new SecureRandom().nextBytes(iv);
rfile.write(iv);
// we have our iv, and there's no prior data to reencrypt
} else {
// file length is at least 2 blocks
rfile.seek(rfile.length()-32); // second to last block
rfile.read(iv); // get iv
byte[] lastBlockEnc = new byte[16];
// last block
// it's padded, so we'll decrypt it and
// save it for the beginning of our data
rfile.read(lastBlockEnc);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
lastBlock = cipher.doFinal(lastBlockEnc);
rfile.seek(rfile.length()-16);
// position ourselves to overwrite the last block
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] out;
if (lastBlock != null) { // lastBlock is null if we're starting a new file
out = cipher.update(lastBlock);
if (out != null) rfile.write(out);
}
out = cipher.doFinal(data);
rfile.write(out);
rfile.close();
}
public static void decryptAES(File file, OutputStream out, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// nothing special here, decrypt as usual
FileInputStream fin = new FileInputStream(file);
byte[] iv = new byte[16];
if (fin.read(iv) < 16) {
throw new IllegalArgumentException("Invalid file length (needs a full block for iv)");
};
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] buff = new byte[1<<13]; //8kiB
while (true) {
int count = fin.read(buff);
if (count == buff.length) {
out.write(cipher.update(buff));
} else {
out.write(cipher.doFinal(buff,0,count));
break;
}
}
fin.close();
}
public static void main(String[] args) throws Exception {
// prep the new encrypted output file reference
File encryptedFileSpec = File.createTempFile("chunked_aes_encrypted.", ".test");
// prep the new decrypted output file reference
File decryptedFileSpec = File.createTempFile("chunked_aes_decrypted.", ".test");
// generate a key spec
byte[] keySpec = new byte[]{0,12,2,8,4,5,6,7, 8, 9, 10, 11, 12, 13, 14, 15};
// for debug/test purposes only, keep track of what's written
StringBuilder plainTextLog = new StringBuilder();
// perform chunked output
for (int i = 0; i<1000; i++) {
// generate random text of variable length
StringBuilder text = new StringBuilder();
Random rand = new Random();
int n = rand.nextInt(5) + 1;
for (int j = 0; j < n; j++) {
text.append(UUID.randomUUID().toString()); // append random string
}
// record it for later comparison
plainTextLog.append(text.toString());
// write it out
byte[] b = text.toString().getBytes("UTF-8");
appendAES(encryptedFileSpec, b, keySpec);
}
System.out.println("Encrypted " + encryptedFileSpec.getAbsolutePath());
// decrypt
decryptAES(encryptedFileSpec, new FileOutputStream(decryptedFileSpec), keySpec);
System.out.println("Decrypted " + decryptedFileSpec.getAbsolutePath());
// compare expected output to actual
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] expectedDigest = md.digest(plainTextLog.toString().getBytes("UTF-8"));
byte[] expectedBytesEncoded = Base64.getEncoder().encode(expectedDigest);
System.out.println("Expected decrypted content: " + new String(expectedBytesEncoded));
byte[] actualBytes = Files.readAllBytes(Paths.get(decryptedFileSpec.toURI()));
byte[] actualDigest = md.digest(actualBytes);
byte[] actualBytesEncoded = Base64.getEncoder().encode(actualDigest);
System.out.println("> Actual decrypted content: " + new String(actualBytesEncoded));
}https://stackoverflow.com/questions/28243879
复制相似问题