-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEncryptor.java
More file actions
93 lines (74 loc) · 3.5 KB
/
Encryptor.java
File metadata and controls
93 lines (74 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
import java.nio.file.*;
import java.security.*;
import java.security.spec.*;
import java.util.*;
public class Encryptor {
// ===== FOLDERS =====
static final Path INPUT_DIR = Paths.get("input");
static final Path ENCRYPTED_DIR = Paths.get("encrypted");
static final Path CLIENT_KEYS = Paths.get("keys/Client");
static final String CLIENT_PUBLIC_KEY = "public.key";
static final Set<String> IGNORE = Set.of(".jpg", ".png");
// ===== CRYPTO CONSTANTS =====
static final int AES_KEY_SIZE = 256;
static final int GCM_IV_SIZE = 12;
static final int GCM_TAG_SIZE = 128;
static final int CHUNK_SIZE = 64 * 1024 * 1024; // 64MB
public static void main(String[] args) throws Exception {
Files.createDirectories(ENCRYPTED_DIR);
PublicKey clientPub = loadPublicKey(CLIENT_KEYS.resolve(CLIENT_PUBLIC_KEY));
Files.walk(INPUT_DIR)
.filter(Files::isRegularFile)
.forEach(file -> {
try {
if (shouldIgnore(file))
return;
encryptFile(file, clientPub);
Files.delete(file);
System.out.println("✔ Encrypted: " + file);
} catch (Exception e) {
System.out.println("❌ Failed: " + file + " (" + e.getMessage() + ")");
}
});
System.out.println("Encryption complete. Exiting...");
}
static void encryptFile(Path file, PublicKey rsaPub) throws Exception {
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(AES_KEY_SIZE);
SecretKey aesKey = gen.generateKey();
Path relative = INPUT_DIR.relativize(file);
Path encFile = ENCRYPTED_DIR.resolve(relative + ".enc");
Files.createDirectories(encFile.getParent());
try (InputStream in = new BufferedInputStream(Files.newInputStream(file));
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(encFile)))) {
byte[] buffer = new byte[CHUNK_SIZE];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
byte[] iv = new byte[GCM_IV_SIZE];
SecureRandom.getInstanceStrong().nextBytes(iv);
Cipher aes = Cipher.getInstance("AES/GCM/NoPadding");
aes.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(GCM_TAG_SIZE, iv));
byte[] encrypted = aes.doFinal(buffer, 0, bytesRead);
out.writeInt(encrypted.length); // store chunk length
out.write(iv); // store IV
out.write(encrypted); // store encrypted chunk + tag
}
}
// Encrypt AES key with RSA
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsa.init(Cipher.ENCRYPT_MODE, rsaPub);
byte[] encKey = rsa.doFinal(aesKey.getEncoded());
Files.write(encFile.resolveSibling(encFile.getFileName() + ".key"), encKey);
}
static boolean shouldIgnore(Path file) {
String n = file.getFileName().toString().toLowerCase();
return IGNORE.stream().anyMatch(n::endsWith);
}
static PublicKey loadPublicKey(Path path) throws Exception {
byte[] b = Base64.getDecoder().decode(Files.readAllBytes(path));
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(b));
}
}