[Java] RSA algorithm - the cornerstone of public key encryption and digital signatures, principle interpretation, code implementation, source code interpretation

In today's digital world, the RSA algorithm is undoubtedly one of the cornerstones of security. It has been keeping our data safe since it was proposed in 1977 by Ron Rivest, Adi Shamir and Leonard Adleman important role.

1. What is the RSA algorithm?

The RSA algorithm is an asymmetric encryption algorithm, that is, it uses two keys: one is the public key and the other is the private key. The public key can be shared publicly, while the private key must be kept secret. The public key is used to encrypt data, while the private key is used to decrypt data. In addition to this, the RSA algorithm can be used to generate digital signatures to verify the integrity and origin of data.

2. Working principle of RSA algorithm

  • Key generation: The RSA algorithm first needs to generate a pair of keys. This is done by choosing two large prime numbers p and q, and calculating their product n = pq. When choosing these two prime numbers, you need to ensure that the factor of their product has no other factors except 1 and themselves. Then calculate the Euler function φ(n)=(p-1)(q-1). Next, choose an integer e such that 1<e<φ(n), and e and φ(n) are relatively prime. Finally, calculate the modular inverse element d of e with respect to φ(n), which satisfies e*d mod φ(n) = 1. The public key is (n,e) and the private key is (n,d).
    Encryption: Encrypt data using public key (n,e). First, convert the plaintext data into an integer m, satisfying 0<=m<n. Then, calculate the ciphertext c, that is, c=m^e mod n.

  • Decryption: Use the private key (n,d) to decrypt the ciphertext. First, calculate the plaintext m, that is, m=c^d mod n.

  • The security of RSA algorithm
    The security of RSA algorithm is based on the difficulty of decomposing large numbers. It is safe in theory, but with the development of quantum computers, the RSA algorithm may no longer be safe in the future. Therefore, to maintain security, it is recommended to use a sufficiently large key length (at least 2048 bits).

3. Java code examples

The following is a simple Java code example showing how to use the RSA algorithm for encryption and decryption:

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import javax.crypto.Cipher;

public class RSAExample {
    
    
    public static void main(String[] args) throws Exception {
    
    

        try {
    
    
            // 生成RSA密钥对
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048, new SecureRandom());
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();


            System.out.println("priveteKey:" + Base64.getEncoder().encode(privateKey.getEncoded()));
            System.out.println("publicKey:" + Base64.getEncoder().encode(publicKey.getEncoded()));

            // 加密明文
            String plaintext = "KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");";
            Cipher encryptCipher = Cipher.getInstance("RSA");
            encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] ciphertext = encryptCipher.doFinal(plaintext.getBytes());

            // 解密密文
            Cipher decryptCipher = Cipher.getInstance("RSA");
            decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] plaintextBytes = decryptCipher.doFinal(ciphertext);
            String decryptedText = new String(plaintextBytes);

            System.out.println("plaintext: " + plaintext);
            System.out.println("encryptCipher: " + ciphertext.length + " size");
            System.out.println("encryptCipherText: " + new String(ciphertext, StandardCharsets.UTF_8));
            System.out.println("decryptCipherText: " + decryptedText);


        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

Output result:

This code first generates an RSA key pair, then encrypts a plaintext string using the public key, and finally decrypts it using the private key. This simple example shows the basic working principle of RSA. However, in practical applications, other security factors need to be considered, such as key management, security protocols, etc.

4. Implement RSA algorithm

RSA is an asymmetric encryption algorithm that uses a pair of keys, one public for encryption and the other secret for decryption. The following is the calculation process of the RSA algorithm:

  • Choose two large prime numbers p and q, and calculate their product n=p*q.
  • Choose a public exponent e, requiring that e and φ(n)=(p-1)*(q-1) (Euler function) are relatively prime, that is, gcd(e, φ(n))=1.
  • Calculate the modulus inverse element d that is relatively prime to e, that is, gcd(d, φ(n))=1 and d*e≡1 mod φ(n).
  • Destroy p and q so that only authorized entities can retrieve them.

Encryption process:

  • For the plaintext message m to be encrypted, convert it into an integer less than n.
  • Encrypt m to get ciphertext c, the calculation formula is c=m^e mod n.
  • Send the ciphertext c to the recipient.

Decryption process:

  • After the receiver receives the ciphertext c, he uses his own private key d to decrypt c and obtains the plaintext m. The calculation formula is m=c^d mod n.
    Since d and φ(n) are mutually prime, according to Fermat's little theorem, c^d mod n=(me)d mod n=(m^(ed)) mod n=m^(ed*mod n) mod n =m.
package com.example.demo;


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

import java.util.LinkedList;
import java.util.List;

public class DemoMain {
    
    
    public static void main(String[] args) throws UnsupportedEncodingException {
    
    


        // 选择两个质数p和q
        BigInteger p = new BigInteger("11");
        BigInteger q = new BigInteger("19");

        // 计算n和φ(n)
        BigInteger n = p.multiply(q);

        BigInteger phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));

        System.out.println("n:" + n);
        System.out.println("phi:" + phi);

        // 选择一个公开的指数e,并计算模反元素d
        BigInteger e = new BigInteger("7");

        BigInteger d = e.modInverse(phi);


        // 公钥和私钥
        BigInteger publicKey = e;
        BigInteger privateKey = d;
        System.out.println("公钥:" + publicKey);

        System.out.println("私钥:" + privateKey);

        // 明文消息
        String plaintext = "kexuexiong";

        //一、私钥加密,公钥解密,模拟服务器发消息给客户端
        System.out.println("-------------------------------------私钥加密,公钥解密,模拟服务器发消息给客户端---------------------------------");

        processing(n, e, d, plaintext);


        //二、公钥加密,私钥解密,模拟客户端发消息给服务器
        System.out.println("-------------------------------------公钥加密,私钥解密,模拟客户端发消息给服务器---------------------------------");
         plaintext = "hello ,rose and jeck!!";
        processing(n, d, e, plaintext);

    }

    private static void processing(BigInteger n, BigInteger e, BigInteger d, String plaintext) throws UnsupportedEncodingException {
    
    

        System.out.println("需要加密的明文:"+plaintext);
        // 加密过程
        byte[] bytes = plaintext.getBytes("utf-8");
        List<String> plaintextList = new LinkedList<>();


        for (Byte aByte : bytes) {
    
    
            BigInteger message = new BigInteger(aByte.toString());

            BigInteger ciphertext = message.modPow(d, n);//加密之后的值可能超过Byte的最大值,所以直接用string保存
            plaintextList.add(ciphertext.toString());
        }

        System.out.println("加密后队列:"+plaintextList);


        // 解密过程
        List<Byte> cipherList = new LinkedList<>();

        for (String ciphertext : plaintextList) {
    
    

            BigInteger decryptedMessage = new BigInteger(ciphertext).modPow(e, n);

            cipherList.add(decryptedMessage.byteValue());

        }
        System.out.println("解密后队列信息: " + cipherList);

        byte[]  bytesMsg = new byte[cipherList.size()];

        for (int i = 0; i < cipherList.size(); i++) {
    
    
            bytesMsg[i] = cipherList.get(i);
        }

        System.out.println("解密后信息:" + new String(bytesMsg, "utf-8"));
    }


}


Output result:

n:209
phi:180
公钥:7
私钥:103
-------------------------------------私钥加密,公钥解密,模拟服务器发消息给客户端---------------------------------
需要加密的明文:kexuexiong
加密后队列:[50, 118, 175, 90, 118, 175, 51, 100, 143, 141]
解密后队列信息: [107, 101, 120, 117, 101, 120, 105, 111, 110, 103]
解密后信息:kexuexiong
-------------------------------------公钥加密,私钥解密,模拟客户端发消息给服务器---------------------------------
需要加密的明文:hello ,rose and jeck!!
加密后队列:[80, 161, 48, 48, 188, 10, 66, 38, 188, 58, 161, 10, 147, 165, 111, 10, 182, 161, 44, 145, 22, 22]
解密后队列信息: [104, 101, 108, 108, 111, 32, 44, 114, 111, 115, 101, 32, 97, 110, 100, 32, 106, 101, 99, 107, 33, 33]
解密后信息:hello ,rose and jeck!!

Process finished with exit code 0

4. Source code comparison and interpretation in Java

RSAKeyPairGenerator.java

 public KeyPair generateKeyPair() {
    
    
        BigInteger e = publicExponent;
        BigInteger minValue = (useNew? getSqrt(keySize) : ZERO);
        int lp = (keySize + 1) >> 1;
        int lq = keySize - lp;
        int pqDiffSize = lp - 100;

        while (true) {
    
    
            BigInteger p = null;
            BigInteger q = null;

            int i = 0;
            while (i++ < 10*lp) {
    
    
                BigInteger tmpP = BigInteger.probablePrime(lp, random);
                if ((!useNew || tmpP.compareTo(minValue) == 1) &&
                        isRelativePrime(e, tmpP.subtract(ONE))) {
    
    
                    p = tmpP;
                    break;
                }
            }
            if (p == null) {
    
    
                throw new ProviderException("Cannot find prime P");
            }

            i = 0;

            while (i++ < 20*lq) {
    
    
                BigInteger tmpQ = BigInteger.probablePrime(lq, random);

                if ((!useNew || tmpQ.compareTo(minValue) == 1) &&
                        (p.subtract(tmpQ).abs().compareTo
                                (TWO.pow(pqDiffSize)) == 1) &&
                        isRelativePrime(e, tmpQ.subtract(ONE))) {
    
    
                    q = tmpQ;
                    break;
                }
            }
            if (q == null) {
    
    
                throw new ProviderException("Cannot find prime Q");
            }

            BigInteger n = p.multiply(q);
            if (n.bitLength() != keySize) {
    
    
                // regenerate P, Q if n is not the right length; should
                // never happen for the new case but check it anyway
                continue;
            }

            KeyPair kp = createKeyPair(type, keyParams, n, e, p, q);
            // done, return the generated keypair;
            if (kp != null) return kp;
        }
    }

RSAKeyPairGenerator.java

  private static KeyPair createKeyPair(KeyType type,
            AlgorithmParameterSpec keyParams,
            BigInteger n, BigInteger e, BigInteger p, BigInteger q) {
    
    
        // phi = (p - 1) * (q - 1) must be relative prime to e
        // otherwise RSA just won't work ;-)
        BigInteger p1 = p.subtract(ONE);
        BigInteger q1 = q.subtract(ONE);
        BigInteger phi = p1.multiply(q1);//欧拉函数

        BigInteger gcd = p1.gcd(q1);//取互质数
        BigInteger lcm = (gcd.equals(ONE)?  phi : phi.divide(gcd));

        BigInteger d = e.modInverse(lcm);

        if (d.compareTo(TWO.pow(p.bitLength())) != 1) {
    
    
            return null;
        }

        // 1st prime exponent pe = d mod (p - 1)
        BigInteger pe = d.mod(p1);
        // 2nd prime exponent qe = d mod (q - 1)
        BigInteger qe = d.mod(q1);
        // crt coefficient coeff is the inverse of q mod p
        BigInteger coeff = q.modInverse(p);

        try {
    
    
            PublicKey publicKey = new RSAPublicKeyImpl(type, keyParams, n, e);
            PrivateKey privateKey = new RSAPrivateCrtKeyImpl(
                type, keyParams, n, e, d, p, q, pe, qe, coeff);
            return new KeyPair(publicKey, privateKey);
        } catch (InvalidKeyException exc) {
    
    
            // invalid key exception only thrown for keys < 512 bit,
            // will not happen here
            throw new RuntimeException(exc);
        }
    }

Guess you like

Origin blog.csdn.net/qq_22744093/article/details/132763531
Recommended