diff --git a/src/Caesar.java b/src/Caesar.java new file mode 100644 index 0000000..a467865 --- /dev/null +++ b/src/Caesar.java @@ -0,0 +1,18 @@ +package com.crypto; +import java.util.Scanner; +public class Caesar { + + public static void encode(Scanner sc){ + String alphabet = "abcdefghijklmnopqrstuvwxyz"; + System.out.println("Digita il testo in chiaro da cifrare:"); + String base = sc.nextLine(); + System.out.println("Digita il valore della chiave:"); + int key = sc.nextInt(); + char encoded[] = base.toCharArray(); + + for(int i = 0; i < base.length(); i++){ + encoded[i] = alphabet.charAt(alphabet.indexOf(encoded[i]) + key % 26); + } + System.out.println(encoded); + } +} diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..de7188f --- /dev/null +++ b/src/Main.java @@ -0,0 +1,140 @@ +package com.crypto; +import java.math.BigInteger; +import java.util.Scanner; + +public class Main { + + // ───────────────────────────────────────────── + // Menu + // ───────────────────────────────────────────── + private static int scegliDimensioneChiave(Scanner scanner) { + int bits = 0; + + while (bits == 0) { + System.out.println("\nSeleziona la dimensione della chiave:"); + System.out.println(" [1] 512 bit (Solo uso didattico)"); + System.out.println(" [2] 1024 bit (Deprecata, solo test)"); + System.out.println(" [3] 2048 bit (Standard attuale)"); + System.out.println(" [4] 3072 bit (Ottimo compromesso)"); + System.out.println(" [5] 4096 bit (Alta sicurezza)"); + System.out.print("Scelta (1-5): "); + + try { + int scelta = Integer.parseInt(scanner.nextLine().trim()); + + // Assegniamo i bit in base alla scelta + switch (scelta) { + case 1 -> bits = 512; + case 2 -> bits = 1024; + case 3 -> bits = 2048; + case 4 -> bits = 3072; + case 5 -> bits = 4096; + default -> System.out.println("❌ Scelta non valida. Inserisci un numero da 1 a 5."); + } + } catch (NumberFormatException e) { + System.out.println("❌ Errore: Inserisci un numero valido."); + } + } + + System.out.println("Selezionata chiave da " + bits + " bit."); + return bits; + } + + // Main + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + System.out.println("=========================================="); + System.out.println(" RSA CRYPTO ENGINE "); + System.out.println("=========================================="); + + // --- 1. SETUP DELLE CHIAVI --- + int dimChiave = scegliDimensioneChiave(scanner); + + System.out.println("\n⏳ Generazione delle chiavi in corso..."); + // Passiamo dimChiave / 2 come facevi prima per p e q + RSA.RSAkeys chiavi = RSA.defkeys(dimChiave / 2); + System.out.println("> Chiavi generate con successo!"); + + boolean continua = true; + + // --- CICLO PRINCIPALE --- + while (continua) { + + System.out.println("\n------------------------------------------"); + System.out.println("Cosa vuoi fare?"); + System.out.println(" [1] Cifra un numero"); + System.out.println(" [2] Cifra una stringa"); + System.out.println(" [3] Rigenera le chiavi"); + System.out.println(" [0] Esci"); + System.out.println("------------------------------------------"); + System.out.print("Scelta: "); + + int scelta = -1; + try { + scelta = Integer.parseInt(scanner.nextLine().trim()); + } catch (NumberFormatException ignored) { + } + + System.out.println(); + + switch (scelta) { + + case 1 -> { + // MODALITÀ NUMERO + System.out.print("Inserisci il numero da cifrare: "); + try { + BigInteger m = new BigInteger(scanner.nextLine().trim()); + + System.out.println("> Cifratura in corso..."); + BigInteger c = RSA.encrypt(m, chiavi.e(), chiavi.n()); + System.out.println("Cifrato:\n" + c); + + System.out.println("\n> Decifratura in corso..."); + BigInteger dec = RSA.decrypt(c, chiavi.d(), chiavi.n()); + System.out.println("Decifrato:\n" + dec); + + } catch (IllegalArgumentException ex) { + System.out.println("> Errore: Assicurati di inserire un numero valido."); + } + } + + case 2 -> { + // MODALITÀ STRINGA + System.out.print("Inserisci il testo da cifrare: "); + String input = scanner.nextLine(); + + System.out.println("> Cifratura in corso..."); + String cifrato = RSA.encrypt(input, chiavi.e(), chiavi.n()); + if (cifrato != null && !cifrato.isEmpty()) { + // 2. SOLO PER LA STAMPA: nascondiamo il binario convertendolo in Base64 + String base64 = java.util.Base64.getEncoder().encodeToString(cifrato.getBytes()); + System.out.println("Cifrato (Base64):\n" + base64); + } + + System.out.println("\n> Decifratura in corso..."); + String dec = RSA.decrypt(cifrato, chiavi.d(), chiavi.n()); + System.out.println("Decifrato:\n" + dec); + } + + case 3 -> { + // RIGENERA CHIAVI + dimChiave = scegliDimensioneChiave(scanner); + System.out.println("\n> Generazione delle chiavi in corso..."); + chiavi = RSA.defkeys(dimChiave / 2); + System.out.println("> Chiavi rigenerate con successo!"); + } + + case 0 -> { + continua = false; + System.out.println("Chiusura del motore RSA. Alla prossima!"); + } + + default -> System.out.println("> Scelta non valida, riprova."); + } + } + + scanner.close(); + } +} + diff --git a/src/RSA.java b/src/RSA.java new file mode 100644 index 0000000..5f3937f --- /dev/null +++ b/src/RSA.java @@ -0,0 +1,93 @@ +package com.crypto; +import java.math.BigInteger; + +public class RSA { + + // Struttura che rappresenta il mazzo di chiavi che viene generato con la + // funzione defkeys() + public record RSAkeys(BigInteger n, BigInteger e, BigInteger d) {} + + static RSAkeys defkeys(int primesize) { + // Generiamo i numeri primi p e q, con la size scelta dall'utente + BigInteger p = Utilities.GenPrime(primesize); + BigInteger q = Utilities.GenPrime(primesize); + + // Rigeneriamo q se p e q sono uguali (just in case) + while (p.equals(q)) { + q = Utilities.GenPrime(primesize); + } + + // Calcoliamo n come prodotto pq + BigInteger n = p.multiply(q); + + // Calcoliamo la funzione di Eulero phi (o m) + BigInteger phi = (p.subtract(BigInteger.ONE)).multiply((q.subtract(BigInteger.ONE))); + + // Calcoliamo la chiave pubblica e, rendendola prima rispetto a phi + BigInteger e = Utilities.CalculateE(phi); + + // Stampa i risultati per il debug + System.out.println("\n--- RISULTATI GENERAZIONE ---"); + System.out.println("Bit di p generato: " + p); + System.out.println("Bit di q generato: " + q); + System.out.println("Bit del modulo n: " + n); + System.out.println("Bit di phi generato: " + phi); + System.out.println("Bit del modulo e: " + e); + + // Calcoliamo la chiave privata d (o k) + BigInteger d = e.modInverse(phi); + + return new RSAkeys(n, e, d); + } + + // ---------------------- Encrypting e Decrypting di valori numerici ---------------------------------- + + static BigInteger encrypt(BigInteger m, BigInteger e, BigInteger n) { + // Controllo vitale: m deve essere positivo e minore di n + if (m.compareTo(BigInteger.ZERO) < 0 || m.compareTo(n) >= 0) { + throw new IllegalArgumentException( + "Il messaggio deve essere positivo e minore del modulo n! - Cifratura annullata"); + } + // Cifriamo con i nostri parametri kpb(e,n) + return m.modPow(e, n); + } + + static BigInteger decrypt(BigInteger c, BigInteger d, BigInteger n) { + // Decifriamo con i nostri parametri kpb(e,n) + return c.modPow(d, n); + } + + // ---------------- Encrypting e Decrypting di valori alfanumerici --------------------------- + + static String encrypt(String input, BigInteger e, BigInteger n) { + // Calcoliamo la lunghezza dei blocchi g e la approssimiamo per difetto + // basta contare i bit necessari per rappresentare n e poi fare - 1 perchè non ci serve la precisione + int g = n.bitLength() - 1; + if (g == 8) + g = 7; + + // Conversione da stringhe a bytes( ASCII ma in decimale !) + byte[] m = input.getBytes(); + + // Conversione in Binario e unificazione in un unica stringa + String data = Utilities.BytestoBin(m); + + // Divisione del messaggio e cifratura + String res = Utilities.DivideetImpera(data,g,e,n); + return res; + } + + static String decrypt(String m, BigInteger d, BigInteger n) { + // Calcolo di z + int z = n.bitLength(); + if (n.and(n.subtract(BigInteger.ONE)).equals(BigInteger.ZERO)) + z--; // n è una potenza esatta di 2, togli 1 + // Calcolo di g + int g = n.bitLength() - 1; + if (g == 8) + g = 7; + String res = Utilities.ImperaetDivide(m, z, g, d, n); + return res.toString(); + } + +} \ No newline at end of file diff --git a/src/Seal.java b/src/Seal.java new file mode 100644 index 0000000..58d24f6 --- /dev/null +++ b/src/Seal.java @@ -0,0 +1,62 @@ +package com.crypto; +import io.javalin.Javalin; + +public class Seal { + + // Il mazzo di chiavi !! + public static RSA.RSAkeys keys; + public static void main(String[] args) { + + // Avvio del server web + Javalin app = Javalin.create(config -> { + config.staticFiles.add("/get"); + }).start(8080); + + + System.out.println("⏳ Generazione chiavi RSA per il server web in corso..."); + + // Cifratura con Cifrario di Cesare + app.post("/seal/caesar/encrypt", ctx -> { + + } + + ); + + // Decifratura con Cifrario di Vigenere + app.post("/seal/vigenere/encrypt", ctx -> { + + } + + ); + + // Cifratura con RSA + app.post("/seal/rsa/encrypt", ctx -> { + // Legge i dati nel body dalla richiesta + Richiesta req = ctx.bodyAsClass(Richiesta.class); + keys = RSA.defkeys(req.keysize / 2); + String ciphertext = RSA.encrypt(req.text,keys.e(), keys.n()); + ctx.result(ciphertext); + }); + + // Decifratura con RSA + app.post("/seal/rsa/decrypt", ctx -> { + // Legge i dati nel body dalla richiesta + Richiesta req = ctx.bodyAsClass(Richiesta.class); + + // Usa il metodo RSA per cifrare + String plaintext = RSA.decrypt(req.text, keys.d(), keys.n()); + + // Restituisce il risultato della cifratura + ctx.result(plaintext.replaceAll("\\\\u0000", "")); + }); + } +} + +// Classe utilizzata per tradurre il JSON - Rappresenta l'oggetto richiesta +class Richiesta { + public int keysize; + public String text; + + // Costruttore della richiesta + public Richiesta() {} +} \ No newline at end of file diff --git a/src/Utilities.java b/src/Utilities.java new file mode 100644 index 0000000..6e94c92 --- /dev/null +++ b/src/Utilities.java @@ -0,0 +1,116 @@ +package com.crypto; +import java.math.BigInteger; +import java.security.SecureRandom; + +public class Utilities { + + static BigInteger GenPrime(int primesize) { + // 0. Inizializza il generatore crittograficamente sicuro di numeri casuali + SecureRandom sr = new SecureRandom(); + + // 1. Definizione della soglia di precisione + int precision = 100; + + // Stampa di attesa + System.out.println("Generazione dei numeri primi in corso... (potrebbe richiedere qualche istante)"); + + // 2. Generazione della chiave + BigInteger prime = new BigInteger(primesize, precision, sr); + + return prime; + } + + // Algoritmo visto in classe rimodellato per java + static BigInteger ModExp(BigInteger a, BigInteger b, BigInteger n) { + BigInteger result = BigInteger.valueOf(1); + a = a.mod(n); + while (b.compareTo(BigInteger.ZERO) > 0) { + // Se b è dispari + if (b.testBit(0)) { + result = result.multiply(a).mod(n); + } + a = (a.multiply(a)).mod(n); + // Dividiamo l'esponente per 2 spostando i bit (pare che sia più rapido in + // questo modo) + b = b.shiftRight(1); + } + return result; + } + + static BigInteger CalculateE(BigInteger phi) { + BigInteger e = BigInteger.valueOf(65537); + // Finchè il GCD (MCD) non è esattamente uguale a uno + while (!(e.gcd(phi).equals(BigInteger.ONE))) { + e = e.add(BigInteger.valueOf(7)); + } + return e; + } + + public static String BytestoBin(byte[] m) { + StringBuilder sr = new StringBuilder(); + for (byte b : m) { + // %8s significa stringa di 8 bit con eventuale padding a sinistra + // b viene messo in and con 0xFF che serve per forzare valori positivi + // il replace aggiunge il padding di zeri al posto degli spazi + sr.append(String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0')); + } + return sr.toString(); + } + + // Converte i valori cifrati in binario e aggiunge il padding a sinistra fino a + // raggiungere il valore z + public static String BigIntToBin(BigInteger val, int g) { + String bin = val.toString(2); + StringBuilder sb = new StringBuilder(); + // aggiungi zeri a sinistra fino a g + for (int i = bin.length(); i < g; i++) { + sb.append('0'); + } + sb.append(bin); + return sb.toString(); + } + + // Divisione in blocchi e applicazione dell'algoritmo visto in classe per la cifratura + + public static String DivideetImpera(String data, int g, BigInteger e, BigInteger n) { + StringBuilder res = new StringBuilder(); + int i; + // Calcolo di z + int z = n.bitLength(); + if (n.and(n.subtract(BigInteger.ONE)).equals(BigInteger.ZERO)) + z--; // n è una potenza esatta di 2, togli 1 + + for (i = 0; (i + g) <= data.length(); i += g) { + String block = data.substring(i, i + g);// prendo il blocco + BigInteger val = new BigInteger(block, 2);// converto in binario + res.append(BigIntToBin(val.modPow(e, n), z)); + } + if (i < data.length()) { + String b = data.substring(i, data.length()); + while (g != b.length()) { + b += '0'; + } + BigInteger val = new BigInteger(b, 2);// converto in binario + res.append(BigIntToBin(val.modPow(e, n), z)); + } + return res.toString(); + } + + // Divisione in blocchi e applicazione dell'algoritmo visto in classe per la decifratura + + public static String ImperaetDivide(String data, int z,int g, BigInteger e, BigInteger n) { + StringBuilder res = new StringBuilder(); + for (int i = 0; (i + z) <= data.length(); i += z) { + BigInteger val = new BigInteger(data.substring(i, i + z), 2); + res.append(BigIntToBin(val.modPow(e, n), g)); + } + StringBuilder testo = new StringBuilder(); + for (int i = 0; i + 8 <= res.length(); i += 8) { + String block = res.substring(i, i + 8); + int ascii = Integer.parseInt(block, 2); + testo.append((char) ascii); + } + return testo.toString(); + } + +} \ No newline at end of file