RSA implemented in JAVA - without using the security library

Posted by HughbertD on Wed, 15 Dec 2021 20:37:24 +0100

The experiment in the school is just learning the cryptography foundation of network security. I just want to write an RSA encryption algorithm. I have seen a lot of RSA encryption codes before, but most of them are implemented using the library. Because I want to better understand, I don't call the security library to implement it once. Of course, if RSA is used in the future, it is better to use the library, After all, the basic functions written by myself are met, but there must be many deficiencies. There are not many BB. The code:

RSA algorithm

1. Randomly select two large prime numbers p and q and keep them confidential;

2. Calculate n=pq and disclose n;

3. Calculation φ (n)=(p-1)(q-1), yes φ (n) Confidentiality;

4. Randomly select a positive integer e, 1 < E< φ (n) And (E, φ (n))=1, e is disclosed;

5. According to ed=1(mod) φ (n)) find d and keep d confidential;

6. Encryption operation: C=Me(mod n);

7. Decryption operation: M=Cd(mod n).

Note: in both encryption and decryption operations, the values of M and C must be less than n, that is, if the plaintext (or ciphertext) is too large, group encryption (or decryption) must be carried out. (this is the key point and a step I didn't complete in this experiment. People are more waste...)

Modular power algorithm

 /**
     * Modular operation
     * @param b
     * @param n
     * @param m
     * @return   b^n(mod m)
     */
    public static BigInteger modExp(BigInteger b, BigInteger n, BigInteger m) {
        BigInteger result = new BigInteger("1");
        b = b.remainder(m);
        do {
            if ((n.remainder(new BigInteger("2"))).compareTo(BigInteger.ONE)==0){
                result=(result.multiply(b)).remainder(m);
            }
            b = (b.multiply(b)).remainder(m);
            n = n.shiftRight(1);
        } while (n != BigInteger.ZERO);
        return result;
    }

Because RSA is a large prime based algorithm, BigInteger class is used. Because long can only meet 64 bits, it may not be able to encrypt a word in the actual situation.

Of course, the Biginteger class has its own modpow function, which can be used to calculate the power of modules, including the following inverse elements. I didn't use it here, but just wrote it myself for better understanding

Modular inverse (inverse element)

 /**
     * Modular inverse operation
     * @param b
     * @param m
     * @return  b^-1(mod m)
     */
    public static BigInteger modInv1(BigInteger b, BigInteger m) {
        if (b.compareTo(m)==1||b.compareTo(m)==0)
            b = b.remainder(m);
        return Gcd(b, m)[1].compareTo(BigInteger.ZERO)==-1 ? Gcd(b, m)[1].add(m) : Gcd(b, m)[1];
    }
    /**
     * Extended Euclidean algorithm
     * <p>(a,b)=ax+by
     * @param a
     * @param b
     * @return  Return a BigInteger array result, result[0]=x, result[1]=y, result[2]=(a,b)
     */
    public static BigInteger[] Gcd(BigInteger a, BigInteger b) {
        if (a.compareTo(b)==-1) {
            BigInteger temp = a;
            a = b;
            b = temp;
        }
        BigInteger[] result = new BigInteger[3];
        if (b.compareTo(BigInteger.ZERO)==0) {
            result[0] = new BigInteger("1");
            result[1] = new BigInteger("0");
            result[2] = a;
            return result;
        }
        BigInteger[] temp = Gcd(b, a.remainder(b));
        result[0] = temp[1];
        result[1] = temp[0].subtract((a.divide(b)).multiply(temp[1]));
        result[2] = temp[2];
        return result;
    }

RSA encryption and decryption

 /**
     * RSA Encryption operation
     */

    public static BigInteger encyrpt(byte[] textByte,BigInteger e,BigInteger n) throws UnsupportedEncodingException {
        BigInteger t = new BigInteger(1,textByte);
        BigInteger C = modExp(t,e,n);//modpow function can be directly used for modular exponentiation algorithm. Manual implementation is adopted here to make it easier to understand the bottom layer
        //        System.out.println(C);
        return C;
    }
    /**
     * RSA Decryption operation
     */
    public static BigInteger dncyrpt(BigInteger C,BigInteger d,BigInteger n) throws UnsupportedEncodingException {
        BigInteger cm = modExp(C,d,n);
        return cm;
    }

Because algorithms are mathematical problems, which are embodied in the form of numbers, what should we do when we want to encrypt a string or a language in practice?

System.out.println("Enter plaintext:");
Scanner input = new Scanner(System.in);
String M = input.next();
System.out.println("Plaintext to be encrypted:"+M);
byte[] textByte = M.getBytes("UTF-8");
        BigInteger C = encyrpt(textByte,e,n);
        byte[] cmessage = C.toByteArray();
        String a = base64.encodeToString(cmessage);
        System.out.println("Encrypted ciphertext:"+a);
        BigInteger cm = dncyrpt(C,d,n);
        byte[] recmessage = cm.toByteArray();
        String encodedText2 = base64.encodeToString(recmessage);
        System.out.println("Plaintext information obtained after decryption:"+new String(base64.decode(encodedText2), "UTF-8"));

Skillfully change the type of function body you set when calling it
Basically byte[] string BigInteger

Code (full)

package secourity;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;

import java.util.Random;
import java.util.Scanner;


import org.apache.commons.codec.binary.Base64;


public class RSA {
    public static void main(String[] args) throws Exception {
        //Plaintext to be encrypted
        System.out.println("Enter plaintext:");
        Scanner input = new Scanner(System.in);
        String M = input.next();
        System.out.println("Plaintext to be encrypted:"+M);
        byte[] textByte = M.getBytes("UTF-8");
        System.out.println("bytelength:"+textByte.length);
        //Generating large prime numbers p,q
        int bitlength = (textByte.length)*8;//byte=8bite. Using this step, the length of n calculated by p and q must be greater than the input plaintext
        Base64 base64 = new Base64();
        BigInteger p = BigInteger.probablePrime(bitlength, new Random());
        BigInteger q = BigInteger.probablePrime(bitlength, new Random());
        //n=p*q number of MODS generating encryption and decryption operation
        BigInteger n = p.multiply(q);
        int num = n.bitLength();
        System.out.println("bit Count Reg:"+num);
        //Generating the output of Euclidean algorithm
        BigInteger r = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        //Generate public key e
        BigInteger e;
        do {
//            e = new BigInteger(new Random().nextInt(r.bitLength() - 1) + 1, new Random());
            e = new BigInteger(900, new Random());
        } while (e.compareTo(r) != -1 || e.gcd(r).intValue() != 1);
        //Generate private key
        BigInteger d = modInv1(e,r);
        //base64 encoding of public and private keys
        byte[] publickey = e.toByteArray();
        String encodedText = base64.encodeToString(publickey);
//        System.out.println(e);
        byte[] pravitekey = d.toByteArray();
        String encodedText1 = base64.encodeToString(pravitekey);
        System.out.println("Public key is:"+encodedText);
        System.out.println("Private key is:"+encodedText1);


        BigInteger C = encyrpt(textByte,e,n);
        byte[] cmessage = C.toByteArray();
        String a = base64.encodeToString(cmessage);
        System.out.println("Encrypted ciphertext:"+a);
        BigInteger cm = dncyrpt(C,d,n);
        byte[] recmessage = cm.toByteArray();
        String encodedText2 = base64.encodeToString(recmessage);
        System.out.println("Plaintext information obtained after decryption:"+new String(base64.decode(encodedText2), "UTF-8"));




    }
    /**
     * RSA Encryption operation
     */

    public static BigInteger encyrpt(byte[] textByte,BigInteger e,BigInteger n) throws UnsupportedEncodingException {
        BigInteger t = new BigInteger(1,textByte);
        BigInteger C = modExp(t,e,n);//modpow function can be directly used for modular exponentiation algorithm. Manual implementation is adopted here to make it easier to understand the bottom layer
        //        System.out.println(C);
        return C;
    }
    /**
     * RSA Decryption operation
     */
    public static BigInteger dncyrpt(BigInteger C,BigInteger d,BigInteger n) throws UnsupportedEncodingException {
        BigInteger cm = modExp(C,d,n);
        return cm;
    }
    /**
     * Modular operation
     * @param b
     * @param n
     * @param m
     * @return   b^n(mod m)
     */
    public static BigInteger modExp(BigInteger b, BigInteger n, BigInteger m) {
        BigInteger result = new BigInteger("1");
        b = b.remainder(m);
        do {
            if ((n.remainder(new BigInteger("2"))).compareTo(BigInteger.ONE)==0){
                result=(result.multiply(b)).remainder(m);
            }
            b = (b.multiply(b)).remainder(m);
            n = n.shiftRight(1);
        } while (n != BigInteger.ZERO);
        return result;
    }
    /**
     * Modular inverse operation
     * @param b
     * @param m
     * @return  b^-1(mod m)
     */
    public static BigInteger modInv1(BigInteger b, BigInteger m) {
        if (b.compareTo(m)==1||b.compareTo(m)==0)
            b = b.remainder(m);
        return Gcd(b, m)[1].compareTo(BigInteger.ZERO)==-1 ? Gcd(b, m)[1].add(m) : Gcd(b, m)[1];
    }
    /**
     * Extended Euclidean algorithm
     * <p>(a,b)=ax+by
     * @param a
     * @param b
     * @return  Return a BigInteger array result, result[0]=x, result[1]=y, result[2]=(a,b)
     */
    public static BigInteger[] Gcd(BigInteger a, BigInteger b) {
        if (a.compareTo(b)==-1) {
            BigInteger temp = a;
            a = b;
            b = temp;
        }
        BigInteger[] result = new BigInteger[3];
        if (b.compareTo(BigInteger.ZERO)==0) {
            result[0] = new BigInteger("1");
            result[1] = new BigInteger("0");
            result[2] = a;
            return result;
        }
        BigInteger[] temp = Gcd(b, a.remainder(b));
        result[0] = temp[1];
        result[1] = temp[0].subtract((a.divide(b)).multiply(temp[1]));
        result[2] = temp[2];
        return result;
    }

}

Because their code habits and proficiency are not very good, the main part of this code is not well encapsulated, but the logic is relatively clear. If it can play a certain learning role, it is the best. Of course, RSA needs to consider more problems in real situations. It is better to call the library in practical applications.

Topics: Java Back-end cryptology