The difference between RSA encryption and signature

Article directory

  • 1. Principle of signature verification
  • 2. RSAUtils tool class
  • 3. Obtain the basic information of the CA certificate through x509Certificate
  • 4. Obtain the public key length through the public key

1. Principle of signature verification

The essence of a signature is actually encryption, but since the signature does not need to be restored to plain text, it can be hashed before encryption. So signature is actually hash + encryption, and signature verification is hash + decryption + comparison.

Signature process: Hash the plain text, splice the header information, encrypt with the private key, and obtain the signature.

Signature verification process: Use the public key to decrypt the signature, then remove the header information, hash the plain text, and compare the two hash values to see if they are the same. If they are the same, the signature verification is successful.
Code writing: Cipher for encryption Signature for signature

Two RSAUtils tool class

package com.example.tr34.application.utils;

import android.util.Base64;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 *date:2020/9/4 0004
 * author:wsm (Administrator)
 * funcation: There is a problem with the private key, base64 decoding failed
 */

public class RSAUtils {
    private static String RSA = "RSA";
    /**
     * **
     * RSA maximum encryption size
     */
    private final static int MAX_ENCRYPT_BLOCK = 117;

    /**
     * **
     * RSA maximum decryption size
     */
    private final static int MAX_DECRYPT_BLOCK = 128;

    /**
     * Randomly generate RSA key pair (default key length is 1024)
     *
     * @return
     */
    public static KeyPair generateRSAKeyPair() {
        return generateRSAKeyPair(1024);
    }

    /**
     * Randomly generate RSA key pair
     *
     * @param keyLength key length, range: 512~2048<br>
     *General 1024
     * @return
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Encrypted with public key<br>
     * The number of bytes encrypted each time cannot exceed the length value of the key minus 11
     *
     * @param data byte data of the data to be encrypted
     * @param publicKey public key
     * @return encrypted byte data
     */
    public static byte[] encryptData(byte[] data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            //Set the encoding method and key before encoding
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // Pass in the encoding data and return the encoding result
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Decrypt with private key
     *
     * @param encryptedData The byte data returned by encryptedData()
     * @param privateKey private key
     * @return
     */
    public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(encryptedData);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Restore the public key through public key byte[](publicKey.getEncoded()), suitable for RSA algorithm
     *
     * @param keyBytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * Restore the public key through the private key byte[], suitable for RSA algorithm
     *
     * @param keyBytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * Use N and e values to restore the public key
     *
     * @param modulus
     * @param publicExponent
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(String modulus, String publicExponent)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * Use N and d values to restore the private key
     *
     * @param modulus
     * @param privateExponent
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(String modulus, String privateExponent)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * Load public key from string
     *
     * @param publicKeyStr public key data string
     * @throws Exception Exception generated when loading the public key
     */
    public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(publicKeyStr,Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("No such algorithm");
        } catch (InvalidKeySpecException e) {
            throw new Exception("Illegal public key");
        } catch (NullPointerException e) {
            throw new Exception("Public key data is empty");
        }
    }

    /**
     * Load private key from string<br>
     * PKCS8EncodedKeySpec (PKCS#8 encoded Key instruction) is used when loading.
     *
     * @param privateKeyStr
     * @return
     * @throwsException
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(privateKeyStr,Base64.DEFAULT);
            // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("No such algorithm");
        } catch (InvalidKeySpecException e) {
            throw new Exception("Illegal private key");
        } catch (NullPointerException e) {
            throw new Exception("Private key data is empty");
        }
    }
    /**
     * Segmented encryption with public key<br>
     * The number of bytes encrypted each time cannot exceed the length value of the key minus 11
     *
     * @param data byte data of the data to be encrypted
     * @param publicKey public key
     * @return encrypted byte data
     */
    public static byte[] PublicSubData(byte[] data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            //Set the encoding method and key before encoding
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // Pass in the encoding data and return the encoding result
// return cipher.doFinal(data);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // Encrypt data segmentally
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK)
                {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else
                {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i + + ;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Decrypt in pieces using private key
     *
     * @param encryptedData The byte data returned by encryptedData()
     * @param privateKey private key
     * @return
     */
    public static byte[] PrivateSubData(byte[] encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
// return cipher.doFinal(encryptedData);
            //Return UTF-8 encoded decryption information
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // Decrypt the data segmentally
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK)
                {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else
                {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i + + ;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * Load the public key from the file input stream
     *
     * @param in public key input stream
     * @throws Exception Exception generated when loading the public key
     */
    public static PublicKey loadPublicKey(InputStream in) throws Exception {
        try {
            return loadPublicKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("Public key data stream reading error");
        } catch (NullPointerException e) {
            throw new Exception("Public key input stream is empty");
        }
    }

    /**
     * Load private key from file
     *
     * @param in private key file name
     * @return whether successful
     * @throwsException
     */
    public static PrivateKey loadPrivateKey(InputStream in) throws Exception {
        try {
            return loadPrivateKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("Private key data reading error");
        } catch (NullPointerException e) {
            throw new Exception("Private key input stream is empty");
        }
    }

    /**
     * Read key information
     *
     * @param in
     * @return
     * @throwsIOException
     */
    private static String readKey(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String readLine = null;
        StringBuilder sb = new StringBuilder();
        while ((readLine = br.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                sb.append(readLine);
                sb.append('\r');
            }
        }

        return sb.toString();
    }

    /**
     * Print public key information
     *
     * @param publicKey
     */
    public static void printPublicKeyInfo(PublicKey publicKey) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        System.out.println("----------RSAPublicKey----------");
        System.out.println("Modulus.length=" + rsaPublicKey.getModulus().bitLength());
        System.out.println("Modulus=" + rsaPublicKey.getModulus().toString());
        System.out.println("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());
        System.out.println("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());
    }

    public static void printPrivateKeyInfo(PrivateKey privateKey) {
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        System.out.println("----------RSAPrivateKey ----------");
        System.out.println("Modulus.length=" + rsaPrivateKey.getModulus().bitLength());
        System.out.println("Modulus=" + rsaPrivateKey.getModulus().toString());
        System.out.println("PrivateExponent.length=" + rsaPrivateKey.getPrivateExponent().bitLength());
        System.out.println("PrivatecExponent=" + rsaPrivateKey.getPrivateExponent().toString());

    }
}




3. Obtain basic information of the CA certificate through x509Certificate

 //Create X509 factory class
     CertificateFactory cf = CertificateFactory.getInstance("X.509");
     //Create certificate object
     X509Certificate oCert = (X509Certificate)cf.generateCertificate(inStream); // inStream certificate incoming data
     inStream.close();
     SimpleDateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd");
      String info = null;
     //Get the certificate version
      info = String.valueOf(oCert.getVersion());
     System.out.println("Certificate version:" + info);
     //Get the certificate serial number
      info = oCert.getSerialNumber().toString(16);
     System.out.println("Certificate serial number:" + info);
     //Get the certificate validity period
      Date beforedate = oCert.getNotBefore();
      info = dateformat.format(beforedate);
     System.out.println("Certificate Effective Date:" + info);
      Date afterdate = oCert.getNotAfter();
      info = dateformat.format(afterdate);
     System.out.println("Certificate expiration date:" + info);
     //Get certificate subject information
      info = oCert.getSubjectDN().getName();
     System.out.println("Certificate owner:" + info);
     //Get certificate issuer information
      info = oCert.getIssuerDN().getName();
     System.out.println("Certificate Issuer:" + info);
     //Get the certificate signature algorithm name
      info = oCert.getSigAlgName();
     System.out.println("Certificate signing algorithm:" + info);
 
 
      byte[] byt = oCert.getExtensionValue("1.2.86.11.7.9");
      String strExt = new String(byt);
     System.out.println("Certificate extension field:" + strExt);
      byt = oCert.getExtensionValue("1.2.86.11.7.1.8");
      String strExt2 = new String(byt);
     System.out.println("Certificate extension field 2:" + strExt2);

4. Get the public key length through the public key

Get the public key through X509Certificate.getPublicKey

 /**
     * Gets the key length of supported keys
     * @param pk PublicKey used to derive the keysize
     * @return -1 if key is unsupported, otherwise a number >= 0. 0 usually means the length can not be calculated,
     * for example if the key is an EC key and the "implicitlyCA" encoding is used.
     */
    public static int getKeyLength(final PublicKey pk) {
        int len = -1;
        if (pk instanceof RSAPublicKey) {
            final RSAPublicKey rsapub = (RSAPublicKey) pk;
            len = rsapub.getModulus().bitLength();
        } else if (pk instanceof JCEECPublicKey) {
            final JCEECPublicKey ecpriv = (JCEECPublicKey) pk;
            final org.bouncycastle.jce.spec.ECParameterSpec spec = ecpriv.getParameters();
            if (spec != null) {
                len = spec.getN().bitLength();
            } else {
                // We support the key, but we don't know the key length
                len = 0;
            }
        } else if (pk instanceof ECPublicKey) {
            final ECPublicKey ecpriv = (ECPublicKey) pk;
            final java.security.spec.ECParameterSpec spec = ecpriv.getParams();
            if (spec != null) {
                len = spec.getOrder().bitLength(); // does this really return something we expect?
            } else {
                // We support the key, but we don't know the key length
                len = 0;
            }
        } else if (pk instanceof DSAPublicKey) {
            final DSAPublicKey dsapub = (DSAPublicKey) pk;
            if ( dsapub.getParams() != null ) {
                len = dsapub.getParams().getP().bitLength();
            } else {
                len = dsapub.getY().bitLength();
            }
        }
        return len;
    }