Implementation of RSA Asymmetric Encryption Algorithm by Java

  • 2021-10-11 18:15:21
  • OfStack

Java Implementation of Directory Public Key and Private Key

Public and private keys

The public key and the private key are paired and 1-like. What we think is public key encryption, private key decryption, private key signature and public key verification. Some people say that private key encryption and public key decryption are wrong.

There are many ways to generate public key and private key, which can be generated by program (implemented below) and through openssl tool:


    #  Generate 1 Private key, recommended 1024 The secret key of the bit, and the secret key is pem Format is saved to -out In the file specified by the parameter, use the PKCS1 Format 
    openssl genrsa -out rsa.pem 1024 
    #  Generates the public key corresponding to the private key, and generates the Subject Public Key , 1 General cooperation PKCS8 Format private key use 
    openssl rsa -in rsa.pem -pubout -out rsa.pub  

There are two formats for generating public key and private key by RSA: PKCS1 and PKCS8. The secret key generated by the above command is PKCS1 format, while the public key is Subject Public Key. 1 is used together with the private key in PKCS8 format, so it may involve the conversion between PKCS1 and PKCS8:


    # PKCS1 Format private key is converted to PKCS8 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
    # PKCS8 Format private key is converted to PKCS1 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # PKCS1 Format public key to PKCS8 Format public key, the converted content is directly output 
    openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
    # PKCS8 Format public key to PKCS1 Format public key, the converted content is directly output 
    openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

In reality, we often obtain public and private keys from pem, crt and pfx files. For the production of crt and pfx, we can refer to: simply make ssl certificates and use them in nginx and IIS, or use the ready-made https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7E42EN (extraction code: c6tj)

Implementation of Java

To simplify the description, here I encapsulate a tool class directly. Because I want to obtain public, private and private keys from pem, crt and pfx files, I refer to a third-party package: BouncyCastle, and I can add dependencies directly in pom. xml:


    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.68</version>
    </dependency>

Or download it on mvn: Jump

RsaUtil. java in simple package:


import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;

import javax.crypto.Cipher;

import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;

public class RsaUtil {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     *  Randomly generate key pairs 
     * 
     * @param usePKCS8
     *             Whether to adopt PKCS8 Fill mode 
     */
    public static Object[] generateRsaKey(boolean usePKCS8) throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
        //  Initialization 
        keyPairGen.initialize(1024, new SecureRandom());
        //  Generate 1 Key pairs, stored in keyPair Medium 
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); //  Get the private key 
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); //  Get the public key 

        //  These two public and private keys are PKCS8 Format 
        byte[] publicKeyBytes = publicKey.getEncoded();
        byte[] privateKeyBytes = privateKey.getEncoded();

        if (!usePKCS8) {
            //  Will PSCK8 Format public and private keys are converted to PKCS1 Format 
            publicKeyBytes = pkcs8ToPkcs1(false, publicKeyBytes);
            privateKeyBytes = pkcs8ToPkcs1(true, privateKeyBytes);
        }

        return new Object[] { publicKeyBytes, privateKeyBytes };
    }

    /**
     *  From Pem File read key pair 
     * 
     * @param reader
     *             Input stream 
     * @param pemFileName
     *            pem Documents 
     */
    public static byte[] readFromPem(String pemFileName) throws Exception {
        PemReader pemReader = new PemReader(new FileReader(pemFileName));
        PemObject pemObject = pemReader.readPemObject();
        byte[] publicKey = pemObject.getContent();
        pemReader.close();
        return publicKey;
    }

    /**
     *  From Pem File read key 
     * 
     * @param isPrivateKey
     *             Is it a private key 
     * @param buffer
     *             Byte 
     * @param pemFileName
     *            pem Documents 
     */
    public static void writeToPem(byte[] buffer, boolean isPrivateKey, String pemFileName) throws Exception {

        PemObject pemObject = new PemObject(isPrivateKey ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY", buffer);
        FileWriter fileWriter = new FileWriter(pemFileName);
        PemWriter pemWriter = new PemWriter(fileWriter);
        pemWriter.writeObject(pemObject);
        pemWriter.close();
    }

    /**
     *  From crt File read public key (pkcs8)
     * 
     * @param crtFileName
     *            crt Documents 
     * @return  Public key 
     */
    public static byte[] readPublicKeyFromCrt(String crtFileName) throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(new FileInputStream(crtFileName));

        PublicKey publicKey = cert.getPublicKey();
        return publicKey.getEncoded();
    }

    /**
     *  From pfx File reading secret key pair (pkcs8)
     * 
     * @param pfxFileName
     *            pfx Documents 
     * @return  Secret key pair 
     */
    public static Object[] readFromPfx(String pfxFileName, String password) throws Exception {
        KeyStore keystore = KeyStore.getInstance("PKCS12");
        char[] passwordChars = null;
        if (password == null || password.equals("")) {
            passwordChars = null;
        } else {
            passwordChars = password.toCharArray();
        }
        keystore.load(new FileInputStream(pfxFileName), passwordChars);
        Enumeration<String> enums = keystore.aliases();

        PrivateKey privateKey = null;
        Certificate certificate = null;
        while (enums.hasMoreElements()) {
            String alias = enums.nextElement();
            System.out.println(alias);
            if (keystore.isKeyEntry(alias)) {
                privateKey = (PrivateKey) keystore.getKey(alias, passwordChars);
                certificate = keystore.getCertificate(alias);
            }
            if (privateKey != null && certificate != null)
                break;
        }
        if (privateKey == null || certificate == null) {
            throw new Exception("fail to read key from pfx");
        }

        PublicKey publicKey = certificate.getPublicKey();
        return new Object[] { publicKey.getEncoded(), privateKey.getEncoded() };
    }

    /**
     * Pkcs8 Turn Pkcs1
     * 
     * @param isPrivateKey
     *             Whether it is a private key conversion 
     * @param buffer
     *            Pkcs1 Secret key 
     * @return Pkcs8 Secret key 
     * @throws Exception
     *              Exception information in encryption process 
     */
    public static byte[] pkcs8ToPkcs1(boolean isPrivateKey, byte[] buffer) throws Exception {
        if (isPrivateKey) {
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(buffer);
            return privateKeyInfo.parsePrivateKey().toASN1Primitive().getEncoded();
        } else {
            SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(buffer);
            return subjectPublicKeyInfo.parsePublicKey().toASN1Primitive().getEncoded();
        }
    }

    /**
     * Pkcs1 Turn Pkcs8
     * 
     * @param isPrivateKey
     *             Whether it is a private key conversion 
     * @param buffer
     *            Pkcs1 Secret key 
     * @return Pkcs8 Secret key 
     * @throws Exception
     *              Exception information in encryption process 
     */
    public static byte[] pkcs1ToPkcs8(boolean isPrivateKey, byte[] buffer) throws Exception {
        AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption);
        ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(buffer);
        if (isPrivateKey) {
            PrivateKeyInfo privateKeyInfo = new PrivateKeyInfo(algorithmIdentifier, asn1Primitive);
            return privateKeyInfo.getEncoded();
        } else {
            SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, asn1Primitive);
            return subjectPublicKeyInfo.getEncoded();
        }
    }

    /**
     * RSA Public key 
     * 
     * @param usePKCS8
     *             Whether to adopt PKCS8 Fill mode 
     * @param publicKey
     *             Public key 
     * @return  Public key 
     * @throws Exception
     *              Exception information in encryption process 
     */
    public static RSAPublicKey generatePublicKey(boolean usePKCS8, byte[] publicKey) throws Exception {
        KeySpec keySpec;
        if (usePKCS8) {
            // PKCS8 Padding 
            keySpec = new X509EncodedKeySpec(publicKey);
        } else {
            // PKCS1 Padding 
            DLSequence sequence = (DLSequence) ASN1Primitive.fromByteArray(publicKey);
            BigInteger v1 = ((ASN1Integer) sequence.getObjectAt(0)).getValue();
            BigInteger v2 = ((ASN1Integer) sequence.getObjectAt(1)).getValue();
            keySpec = new RSAPublicKeySpec(v1, v2);
        }

        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).generatePublic(keySpec);
        return pubKey;
    }

    /**
     * RSA Private key 
     * 
     * @param usePKCS8
     *             Whether to adopt PKCS8 Fill mode 
     * @param privateKey
     *             Private key 
     * @return  Private key 
     * @throws Exception
     *              Abnormal information in decryption process 
     */
    public static RSAPrivateKey generatePrivateKey(boolean usePKCS8, byte[] privateKey) throws Exception {
        KeySpec keySpec;
        if (usePKCS8) {
            // PKCS8 Padding 
            keySpec = new PKCS8EncodedKeySpec(privateKey);
        } else {
            // PKCS1 Padding 
            DLSequence sequence = (DLSequence) ASN1Primitive.fromByteArray(privateKey);
            // BigInteger v1= ((ASN1Integer)sequence.getObjectAt(0)).getValue();
            BigInteger v2 = ((ASN1Integer) sequence.getObjectAt(1)).getValue();
            BigInteger v3 = ((ASN1Integer) sequence.getObjectAt(2)).getValue();
            BigInteger v4 = ((ASN1Integer) sequence.getObjectAt(3)).getValue();
            BigInteger v5 = ((ASN1Integer) sequence.getObjectAt(4)).getValue();
            BigInteger v6 = ((ASN1Integer) sequence.getObjectAt(5)).getValue();
            BigInteger v7 = ((ASN1Integer) sequence.getObjectAt(6)).getValue();
            BigInteger v8 = ((ASN1Integer) sequence.getObjectAt(7)).getValue();
            BigInteger v9 = ((ASN1Integer) sequence.getObjectAt(8)).getValue();
            keySpec = new RSAPrivateCrtKeySpec(v2, v3, v4, v5, v6, v7, v8, v9);
        }

        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).generatePrivate(keySpec);
        return priKey;
    }

    /**
     * RSA Public key encryption 
     * 
     * @param value
     *             Encrypted string 
     * @param publicKey
     *             Public key 
     * @return  Ciphertext 
     * @throws Exception
     *              Exception information in encryption process 
     */
    public static String rsaEncrypt(String value, RSAPublicKey publicKey) throws Exception {
        if (value == null || value.length() == 0)
            return "";

        // RSA Encryption 
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] buffer = cipher.doFinal(value.getBytes("utf-8"));

        //  Use hex Format output public key 
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < buffer.length; i++) {
            result.append(String.format("%02x", buffer[i]));
        }
        return result.toString();
    }

    /**
     * RSA Private key decryption 
     * 
     * @param value
     *             Encrypted string 
     * @param privateKey
     *             Private key 
     * @return  Cleartext 
     * @throws Exception
     *              Abnormal information in decryption process 
     */
    public static String rsaDecrypt(String value, RSAPrivateKey privateKey) throws Exception {
        if (value == null || value.length() == 0)
            return "";

        byte[] buffer = new byte[value.length() / 2];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = (byte) Integer.parseInt(value.substring(i * 2, i * 2 + 2), 16);
        }

        // RSA Decryption 
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        buffer = cipher.doFinal(buffer);
        return new String(buffer, "utf-8");
    }

    /**
     * RSA Signature 
     * 
     * @param value
     *             Encrypted string 
     * @param privateKey
     *             Private key 
     * @param halg
     *             Encryption algorithms, such as MD5, SHA1, SHA256, SHA384, SHA512 Etc 
     * @return  Signature 
     * @throws Exception
     *              Exception information in signing process 
     */
    public static String sign(String value, RSAPrivateKey privateKey, String halg) throws Exception {

        Signature s = Signature.getInstance(halg.toUpperCase().endsWith("WithRSA") ? halg : (halg + "WithRSA"));

        s.initSign(privateKey);
        s.update(value.getBytes("utf-8"));

        byte[] buffer = s.sign();

        //  Use hex Format output public key 
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < buffer.length; i++) {
            result.append(String.format("%02x", buffer[i]));
        }
        return result.toString();
    }

    /**
     * RSA Signature verification 
     * 
     * @param value
     *             Encrypted string 
     * @param publicKey
     *             Public key 
     * @param halg
     *             Encryption algorithms, such as MD5, SHA1, SHA256, SHA384, SHA512 Etc 
     * @return  If the signature is legal, it will return true Otherwise, return false
     * @throws Exception
     *              Exception information during validation 
     */
    public static boolean verify(String value, RSAPublicKey publicKey, String signature, String halg) throws Exception {
        Signature s = Signature.getInstance(halg.toUpperCase().endsWith("WithRSA") ? halg : (halg + "WithRSA"));
        s.initVerify(publicKey);
        s.update(value.getBytes("utf-8"));

        byte[] buffer = new byte[signature.length() / 2];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = (byte) Integer.parseInt(signature.substring(i * 2, i * 2 + 2), 16);
        }

        return s.verify(buffer);
    }

}

Generate public and private keys:


    //  Generate public and private keys 
    Object[] rsaKey = RsaUtil.generateRsaKey(usePKCS8); //usePKCS8=true Indicates whether it is PKCS8 Public and private secret keys of format , Otherwise ride PKCS1 Public and private secret keys of format 
    byte[] publicKey = (byte[]) rsaKey[0];
    byte[] privateKey = (byte[]) rsaKey[1];

After generating the secret key, you need to save it, and save it to pem file:


    //  Save to pem Documents, filePath Is to save the directory 
    RsaUtil.writeToPem(publicKey, false, filePath + "rsa.pub");
    RsaUtil.writeToPem(privateKey, true, filePath + "rsa.pem");

You can save it to the pem file, or you can read it from the pem file:


    //  From Pem File reads public and private keys, filePath Is a file directory 
    byte[] publicKey = RsaUtil.readFromPem(filePath + "rsa.pub");
    byte[] privateKey = RsaUtil.readFromPem(filePath + "rsa.pem");

You can also read the public key from the crt certificate, while the crt file does not contain the private key, so you need to obtain the private key separately:


    //  From crt File read public key ( crt The file does not contain the private key, filePath Is a file directory 
    byte[] publicKey = RsaUtil.readPublicKeyFromCrt(filePath + "demo.crt");
    byte[] privateKey = RsaUtil.readFromPem(filePath + "demo.key");

The pfx file contains the public key and the private key, which can be easily read:


    //  From pfx File reads public and private keys, filePath Is a file directory 
    Object[] rsaKey = RsaUtil.readFromPfx(filePath + "demo.pfx", "123456");
    byte[] publicKey = (byte[]) rsaKey[0];
    byte[] privateKey = (byte[]) rsaKey[1];

Sometimes we may need to convert the secret key:


    // Pkcs8 Format public key to Pkcs1 Format public key 
    publicKey = RsaUtil.pkcs8ToPkcs1(false, publicKey);
    // Pkcs8 Format private key is converted to Pkcs1 Format private key 
    privateKey = RsaUtil.pkcs8ToPkcs1(true, privateKey);
    // Pkcs1 Format public key to Pkcs8 Format public key 
    publicKey = RsaUtil.pkcs1ToPkcs8(false, publicKey);
    // Pkcs1 Format private key is converted to Pkcs8 Format private key 
    privateKey = RsaUtil.pkcs1ToPkcs8(true, privateKey);

With the public key and private key, you can realize encryption, decryption, signature, signature verification and other operations:


    # PKCS1 Format private key is converted to PKCS8 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
    # PKCS8 Format private key is converted to PKCS1 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # PKCS1 Format public key to PKCS8 Format public key, the converted content is directly output 
    openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
    # PKCS8 Format public key to PKCS1 Format public key, the converted content is directly output 
    openssl rsa -RSAPublicKey_in -pubout -in rsa.pub
0

The complete demo code here:


    # PKCS1 Format private key is converted to PKCS8 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
    # PKCS8 Format private key is converted to PKCS1 Format private key, which is directly output to -out Parameter is specified in the file 
    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # PKCS1 Format public key to PKCS8 Format public key, the converted content is directly output 
    openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
    # PKCS8 Format public key to PKCS1 Format public key, the converted content is directly output 
    openssl rsa -RSAPublicKey_in -pubout -in rsa.pub
1

The above is Java implementation of RSA asymmetric encryption algorithm details, more information about Java RSA asymmetric encryption algorithm please pay attention to other related articles on this site!


Related articles: