/
Dixix404
/
Using_TLS
Обзор
Документация
Войти
/
Dixix404
/
Using_TLS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
SendApp/src/main/java/org/example/SendApp.java
205 строк
7 KB
D1x1x
Using TLS
25 дек 2025, 22:33
25 дек 2025, 22:33
43fe136
Код
Авторство
О чём код?
package org.example; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; public class SendApp { private static final byte[] MAGIC_DH = new byte[]{'D','H','F','T'}; private static final byte[] MAGIC_FILE = new byte[]{'E','N','C','F'}; private static final int VERSION = 1; private static final int DH_BITS = 2048; private static final int AES_KEY_BYTES = 32; private static final int GCM_TAG_BITS = 128; private static final int IV_BYTES = 12; private static final byte REC_UPDATE = 1; private static final byte REC_FINAL = 2; private static final int CHUNK = 64 * 1024; public static void main(String[] args) { if (args.length != 2) { System.err.println("Usage: send <address:port> <file-path>"); System.exit(1); } HostPort hp = parseAddress(args[0]); if (hp == null) { System.err.println("Invalid address:port: " + args[0]); System.exit(1); } Path filePath = Path.of(args[1]); if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { System.err.println("File does not exist: " + filePath); System.exit(1); } long fileSize; try { fileSize = Files.size(filePath); } catch (IOException e) { System.err.println("Cannot read file size: " + e.getMessage()); System.exit(1); return; } try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(hp.host, hp.port), 7000); socket.setTcpNoDelay(true); try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream())); InputStream fileIn = Files.newInputStream(filePath)) { byte[] sharedSecret = dhClientHandshake(out, in); SecretKey aesKey = hkdfSha256ToAesKey(sharedSecret, "Using_TLS AES key", AES_KEY_BYTES); byte[] iv = new byte[IV_BYTES]; SecureRandom.getInstanceStrong().nextBytes(iv); Cipher enc = Cipher.getInstance("AES/GCM/NoPadding"); enc.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(GCM_TAG_BITS, iv)); // FILE header out.write(MAGIC_FILE); out.writeInt(VERSION); out.writeLong(fileSize); out.writeInt(iv.length); out.write(iv); // records byte[] buf = new byte[CHUNK]; int r; while ((r = fileIn.read(buf)) != -1) { byte[] ct = enc.update(buf, 0, r); if (ct != null && ct.length > 0) { out.writeByte(REC_UPDATE); out.writeInt(ct.length); out.write(ct); } } byte[] finalCt = enc.doFinal(); out.writeByte(REC_FINAL); out.writeInt(finalCt.length); out.write(finalCt); out.flush(); int ack = in.readInt(); if (ack != 0) { System.err.println("Receiver reported error, code=" + ack); System.exit(1); } } } catch (EOFException e) { System.err.println("Connection closed early (EOF). File not fully transferred."); System.exit(1); } catch (IOException e) { System.err.println("I/O error during transfer: " + e.getMessage()); System.exit(1); } catch (GeneralSecurityException e) { System.err.println("Crypto error: " + e.getMessage()); System.exit(1); } } private static byte[] dhClientHandshake(DataOutputStream out, DataInputStream in) throws GeneralSecurityException, IOException { AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH"); paramGen.init(DH_BITS); AlgorithmParameters params = paramGen.generateParameters(); DHParameterSpec dhSpec = params.getParameterSpec(DHParameterSpec.class); KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH"); kpg.initialize(dhSpec); KeyPair kp = kpg.generateKeyPair(); // send DH hello out.write(MAGIC_DH); out.writeInt(VERSION); writeBytes(out, dhSpec.getP().toByteArray()); writeBytes(out, dhSpec.getG().toByteArray()); writeBytes(out, kp.getPublic().getEncoded()); out.flush(); // receive server pub byte[] serverPubEnc = readBytes(in); PublicKey serverPub = KeyFactory.getInstance("DH").generatePublic(new X509EncodedKeySpec(serverPubEnc)); KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(kp.getPrivate()); ka.doPhase(serverPub, true); return ka.generateSecret(); } private static SecretKey hkdfSha256ToAesKey(byte[] ikm, String info, int keyLenBytes) throws GeneralSecurityException { byte[] salt = new byte[32]; // нулевой salt (для учебного проекта) Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(salt, "HmacSHA256")); byte[] prk = mac.doFinal(ikm); mac.init(new SecretKeySpec(prk, "HmacSHA256")); byte[] t1 = mac.doFinal(concat(new byte[0], info.getBytes(StandardCharsets.UTF_8), new byte[]{1})); return new SecretKeySpec(Arrays.copyOf(t1, keyLenBytes), "AES"); } private static byte[] concat(byte[] a, byte[] b, byte[] c) { byte[] r = new byte[a.length + b.length + c.length]; System.arraycopy(a, 0, r, 0, a.length); System.arraycopy(b, 0, r, a.length, b.length); System.arraycopy(c, 0, r, a.length + b.length, c.length); return r; } private static void writeBytes(DataOutputStream out, byte[] data) throws IOException { out.writeInt(data.length); out.write(data); } private static byte[] readBytes(DataInputStream in) throws IOException { int len = in.readInt(); if (len < 0 || len > 50_000_000) throw new IOException("Invalid length: " + len); byte[] data = new byte[len]; in.readFully(data); return data; } private static HostPort parseAddress(String addressPort) { int idx = addressPort.lastIndexOf(':'); if (idx <= 0 || idx == addressPort.length() - 1) return null; String host = addressPort.substring(0, idx).trim(); String portStr = addressPort.substring(idx + 1).trim(); if (host.isEmpty()) return null; try { int port = Integer.parseInt(portStr); if (port < 1 || port > 65535) return null; return new HostPort(host, port); } catch (NumberFormatException e) { return null; } } private record HostPort(String host, int port) {} }