Feb 05, 2025

Rabin Cryptosystem

Compute non-deterministic versions of the encrypted message provided by the user.

Source Code

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Scanner;

class Cryptography {
    private static SecureRandom r = new SecureRandom();
    private static BigInteger TWO = BigInteger.valueOf(2);
    private static BigInteger FOUR = BigInteger.valueOf(4);

    public static BigInteger[] generateKey(int bitLength) {
        BigInteger p = blumPrime(bitLength / 2);
        BigInteger q = blumPrime(bitLength / 2);
        BigInteger N = p.multiply(q);
        return new BigInteger[] { N, p, q };
    }

    public static BigInteger encrypt(BigInteger m, BigInteger N) {
        return m.modPow(TWO, N);
    }

    public static BigInteger[] decrypt(BigInteger c, BigInteger p, BigInteger q) {
        BigInteger N = p.multiply(q);
        BigInteger p1 = c.modPow(p.add(BigInteger.ONE).divide(FOUR), p);
        BigInteger p2 = p.subtract(p1);
        BigInteger q1 = c.modPow(q.add(BigInteger.ONE).divide(FOUR), q);
        BigInteger q2 = q.subtract(q1);

        BigInteger[] ext = Gcd(p, q);
        BigInteger y_p = ext[1];
        BigInteger y_q = ext[2];

        BigInteger d1 = y_p.multiply(p).multiply(q1).add(y_q.multiply(q).multiply(p1)).mod(N);
        BigInteger d2 = y_p.multiply(p).multiply(q2).add(y_q.multiply(q).multiply(p1)).mod(N);
        BigInteger d3 = y_p.multiply(p).multiply(q1).add(y_q.multiply(q).multiply(p2)).mod(N);
        BigInteger d4 = y_p.multiply(p).multiply(q2).add(y_q.multiply(q).multiply(p2)).mod(N);

        return new BigInteger[] { d1, d2, d3, d4 };
    }

    public static BigInteger[] Gcd(BigInteger a, BigInteger b) {
        BigInteger s = BigInteger.ZERO;
        BigInteger old_s = BigInteger.ONE;
        BigInteger t = BigInteger.ONE;
        BigInteger old_t = BigInteger.ZERO;
        BigInteger r = b;
        BigInteger old_r = a;
        while (!r.equals(BigInteger.ZERO)) {
            BigInteger q = old_r.divide(r);
            BigInteger tr = r;
            r = old_r.subtract(q.multiply(r));
            old_r = tr;

            BigInteger ts = s;
            s = old_s.subtract(q.multiply(s));
            old_s = ts;

            BigInteger tt = t;
            t = old_t.subtract(q.multiply(t));
            old_t = tt;
        }
        return new BigInteger[] { old_r, old_s, old_t };
    }

    public static BigInteger blumPrime(int bitLength) {
        BigInteger p;
        do {
            p = BigInteger.probablePrime(bitLength, r);
        } while (!p.mod(FOUR).equals(BigInteger.valueOf(3)));
        return p;
    }
}

public class RabinCryptosystem {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        BigInteger[] key = Cryptography.generateKey(512);
        BigInteger n = key[0];
        BigInteger p = key[1];
        BigInteger q = key[2];

        System.out.print("Enter a number to encrypt: ");
        BigInteger m = scanner.nextBigInteger();
        BigInteger c = Cryptography.encrypt(m, n);

        System.out.println("Encrypted Number: " + c);

        BigInteger[] decryptedNumbers = Cryptography.decrypt(c, p, q);
        System.out.println("Decrypted Numbers: ");
        for (BigInteger b : decryptedNumbers) {
            System.out.println(b);
        }

        scanner.close();
    }
}