mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
Encrypt websites with AES.
This ensures that nodes are storing unreadable files, outside of the context of Qortal. For public data, the decryption keys themselves are on-chain, included in the "secret" field of arbitrary transactions. When we introduce the concept of private data, we can simply exclude the secret key from the transaction so that only the owner can decrypt it. When encrypting the file, I have added the 16 byte initialization vector as a prefix to the cyphertext, and it is then automatically extracted back out when decrypting. This gives us the option to encrypt more than one file with the same key, if we ever need it. Right now, we are using a unique key per file, so it's not actually needed, but it's good to have support.
This commit is contained in:
@@ -3,6 +3,7 @@ package org.qortal.test;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.crypto.AES;
|
||||
import org.qortal.crypto.BouncyCastle25519;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.test.common.Common;
|
||||
@@ -10,7 +11,17 @@ import org.qortal.utils.Base58;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bouncycastle.crypto.agreement.X25519Agreement;
|
||||
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
|
||||
@@ -20,6 +31,11 @@ import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
public class CryptoTests extends Common {
|
||||
|
||||
@Test
|
||||
@@ -255,4 +271,68 @@ public class CryptoTests extends Common {
|
||||
assertEquals(expectedProxyPrivateKey, Base58.encode(proxyPrivateKey));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAESFileEncryption() throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException,
|
||||
InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException {
|
||||
|
||||
// Create temporary directory and file paths
|
||||
java.nio.file.Path tempDir = Files.createTempDirectory("qortal-tests");
|
||||
String inputFilePath = tempDir.toString() + File.separator + "inputFile";
|
||||
String outputFilePath = tempDir.toString() + File.separator + "outputFile";
|
||||
String decryptedFilePath = tempDir.toString() + File.separator + "decryptedFile";
|
||||
String reencryptedFilePath = tempDir.toString() + File.separator + "reencryptedFile";
|
||||
|
||||
// Generate some dummy data
|
||||
byte[] randomBytes = new byte[1024];
|
||||
new Random().nextBytes(randomBytes);
|
||||
|
||||
// Write it to the input file
|
||||
FileOutputStream outputStream = new FileOutputStream(inputFilePath);
|
||||
outputStream.write(randomBytes);
|
||||
|
||||
// Make sure only the input file exists
|
||||
assertTrue(Files.exists(Paths.get(inputFilePath)));
|
||||
assertFalse(Files.exists(Paths.get(outputFilePath)));
|
||||
|
||||
// Encrypt
|
||||
SecretKey aesKey = AES.generateKey(256);
|
||||
AES.encryptFile("AES", aesKey, inputFilePath, outputFilePath);
|
||||
assertTrue(Files.exists(Paths.get(outputFilePath)));
|
||||
byte[] encryptedBytes = Files.readAllBytes(Paths.get(outputFilePath));
|
||||
|
||||
// Delete the input file
|
||||
Files.delete(Paths.get(inputFilePath));
|
||||
assertFalse(Files.exists(Paths.get(inputFilePath)));
|
||||
|
||||
// Decrypt
|
||||
String encryptedFilePath = outputFilePath;
|
||||
assertFalse(Files.exists(Paths.get(decryptedFilePath)));
|
||||
AES.decryptFile("AES", aesKey, encryptedFilePath, decryptedFilePath);
|
||||
assertTrue(Files.exists(Paths.get(decryptedFilePath)));
|
||||
|
||||
// Delete the output file
|
||||
Files.delete(Paths.get(outputFilePath));
|
||||
assertFalse(Files.exists(Paths.get(outputFilePath)));
|
||||
|
||||
// Check that the decrypted file contents matches the original data
|
||||
byte[] decryptedBytes = Files.readAllBytes(Paths.get(decryptedFilePath));
|
||||
assertTrue(Arrays.equals(decryptedBytes, randomBytes));
|
||||
assertEquals(1024, decryptedBytes.length);
|
||||
|
||||
// Write the original data back to the input file
|
||||
outputStream = new FileOutputStream(inputFilePath);
|
||||
outputStream.write(randomBytes);
|
||||
|
||||
// Now encrypt the data one more time using the same key
|
||||
// This is to ensure the initialization vector produces a different result
|
||||
AES.encryptFile("AES", aesKey, inputFilePath, reencryptedFilePath);
|
||||
assertTrue(Files.exists(Paths.get(reencryptedFilePath)));
|
||||
|
||||
// Make sure the ciphertexts do not match
|
||||
byte[] reencryptedBytes = Files.readAllBytes(Paths.get(reencryptedFilePath));
|
||||
assertFalse(Arrays.equals(encryptedBytes, reencryptedBytes));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user