/* X509CertRequest.java
 *
 * Copyright (C) 2006-2025 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

import java.io.File;
import java.io.IOException;

import java.nio.file.Files;
import java.nio.file.Paths;

import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.cert.CertificateException;
import java.security.NoSuchAlgorithmException;

import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLCertRequest;
import com.wolfssl.WolfSSLX509Name;
import com.wolfssl.WolfSSLException;
import com.wolfssl.WolfSSLJNIException;

/**
 * Example application that demonstrates X509 Certificate Signing Request (CSR)
 * generation including various combinations.
 *
 *     CSR using files as input for certs/keys
 *     CSR using arrays as input for certs/keys
 *     CSR using generated certs and keys
 *
 * Each sub-example is contained in a separate method.
 *
 * When run, generated certificates are written out to PEM and DER files,
 * with location specified by variables at the top of this class.
 *
 */
public class X509CertRequest {

    private static String CERT_DIR = "../certs/";
    private static String GEN_DIR = CERT_DIR + "generated/";
    private static String CERT_DIR_FROM_ROOT = "./exammples/certs/generated/";

    /* Existing certs/keys used for CSR gen example with files */
    private static String clientKeyDer    = CERT_DIR + "client-key.der";
    private static String clientKeyPubDer = CERT_DIR + "client-keyPub.der";

    /* Generated certificate signing request (CSR) locations. */
    private static String csrUsingFilesDer =
        GEN_DIR + "csr-using-files.der";
    private static String csrUsingFilesPem =
        GEN_DIR + "csr-using-files.pem";
    private static String csrUsingArraysDer =
        GEN_DIR + "csr-using-arrays.der";
    private static String csrUsingArraysPem =
        GEN_DIR + "csr-using-arrays.pem";
    private static String csrUsingGeneratedKeysDer =
        GEN_DIR + "csr-generated-keys.der";
    private static String csrUsingGeneratedKeysPem =
        GEN_DIR + "csr-generated-keys.pem";

    /* Example Extension values */
    private static String test_KEY_USAGE =
        "digitalSignature,keyEncipherment,dataEncipherment";
    private static String test_EXT_KEY_USAGE =
        "clientAuth,serverAuth";
    private static String test_ALT_NAME =
        "alt.example.com";

    /* Example Attribute values */
    private static String test_CHALLENGE_PASSWORD =
        "12345!@#$%";

    private void writeFile(String path, byte[] bytes)
        throws IOException {

        File genDir = new File(GEN_DIR);
        if (!genDir.exists()) {
            genDir.mkdir();
        }
        Files.write(new File(path).toPath(), bytes);
    }

    private WolfSSLX509Name generateTestSubjectName()
        throws WolfSSLException {

        WolfSSLX509Name subjectName = new WolfSSLX509Name();
        subjectName.setCountryName("US");
        subjectName.setStateOrProvinceName("Montana");
        subjectName.setStreetAddress("12345 Test Address");
        subjectName.setLocalityName("Bozeman");
        subjectName.setSurname("Test Surname");
        subjectName.setCommonName("example.com");
        subjectName.setEmailAddress("support@example.com");
        subjectName.setOrganizationName("wolfSSL Inc.");
        subjectName.setOrganizationalUnitName("Test and Development");
        subjectName.setPostalCode("59715");
        subjectName.setUserId("TestUserID");

        return subjectName;
    }

    /**
     * Generate example Certificate Signing Request (CSR) using the following
     * files as input to the CSR generation process:
     *
     * clientKeyPubDer - Existing client public key in DER format
     * clientKeyDer    - Existing client private key in DER format
     *
     * Generates and writes CSR out to the following paths in
     * both PEM and DER format (see variable values above):
     *     csrUsingFilesDer (DER format)
     *     csrUsingFilesPem (PEM format)
     *
     * @throws WolfSSLException if error occurs during CSR generation process.
     * @throws WolfSSLJNIException if native JNI error occurs
     * @throws IOException on error writing to output file locations
     */
    public void generateCSRUsingFiles()
        throws WolfSSLException, WolfSSLJNIException, IOException,
               CertificateException {

        System.out.print("\nGenerating CSR using files");

        /* Create new CSR object */
        WolfSSLCertRequest req = new WolfSSLCertRequest();

        /* Set Subject Name */
        WolfSSLX509Name subjectName = generateTestSubjectName();
        req.setSubjectName(subjectName);

        /* Set Public Key from existing public key DER file */
        req.setPublicKey(clientKeyPubDer, WolfSSL.RSAk,
                WolfSSL.SSL_FILETYPE_ASN1);

        /* Add Attributes */
        req.addAttribute(WolfSSL.NID_pkcs9_challengePassword,
                test_CHALLENGE_PASSWORD.getBytes());

        /* Add Extensions */
        req.addExtension(WolfSSL.NID_key_usage, test_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_ext_key_usage, test_EXT_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_subject_alt_name, test_ALT_NAME, false);
        req.addExtension(WolfSSL.NID_basic_constraints, true, true);

        /* Sign CSR, using existing client key DER */
        req.signRequest(clientKeyDer, WolfSSL.RSAk,
                WolfSSL.SSL_FILETYPE_ASN1, "SHA256");

        /* Output to DER and PEM files */
        byte[] derCsr = req.getDer();
        byte[] pemCsr = req.getPem();

        /* Write out generated CSRs to files */
        writeFile(csrUsingFilesDer, derCsr);
        writeFile(csrUsingFilesPem, pemCsr);

        System.out.println("... ");
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingFilesDer).getFileName());
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingFilesPem).getFileName());

        /* Free native memory */
        subjectName.free();
        req.free();
    }

    /**
     * Generate example Certificate Signing Request (CSR) using the following
     * files in array format as input to the CSR generation process:
     *
     * clientKeyPubDer - Existing client public key in DER format
     * clientKeyDer    - Existing client private key in DER format
     *
     * Generates and writes CSR out to the following paths in
     * both PEM and DER format (see variable values above):
     *     csrUsingArraysDer (DER format)
     *     csrUsingArraysPem (PEM format)
     *
     * @throws WolfSSLException if error occurs during CSR generation process.
     * @throws WolfSSLJNIException if native JNI error occurs
     * @throws IOException on error writing to output file locations
     */
    public void generateCSRUsingArrays()
        throws WolfSSLException, WolfSSLJNIException, IOException,
               CertificateException {

        System.out.print("\nGenerating CSR using arrays");

        /* Create new CSR object */
        WolfSSLCertRequest req = new WolfSSLCertRequest();

        /* Set Subject Name */
        WolfSSLX509Name subjectName = generateTestSubjectName();
        req.setSubjectName(subjectName);

        /* Set Public Key from existing public key DER file */
        byte[] pubKey = Files.readAllBytes(Paths.get(clientKeyPubDer));
        req.setPublicKey(pubKey, WolfSSL.RSAk, WolfSSL.SSL_FILETYPE_ASN1);

        /* Add Attributes */
        req.addAttribute(WolfSSL.NID_pkcs9_challengePassword,
                test_CHALLENGE_PASSWORD.getBytes());

        /* Add Extensions */
        req.addExtension(WolfSSL.NID_key_usage, test_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_ext_key_usage, test_EXT_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_subject_alt_name, test_ALT_NAME, false);
        req.addExtension(WolfSSL.NID_basic_constraints, true, true);

        /* Sign CSR, using existing client key DER */
        byte[] privKey = Files.readAllBytes(Paths.get(clientKeyDer));
        req.signRequest(privKey, WolfSSL.RSAk,
                WolfSSL.SSL_FILETYPE_ASN1, "SHA256");

        /* Output to DER and PEM files */
        byte[] derCsr = req.getDer();
        byte[] pemCsr = req.getPem();

        /* Write out generated CSRs to files */
        writeFile(csrUsingArraysDer, derCsr);
        writeFile(csrUsingArraysPem, pemCsr);

        System.out.println("... ");
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingArraysDer).getFileName());
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingArraysPem).getFileName());

        /* Free native memory */
        subjectName.free();
        req.free();
    }

    /**
     * Generate example Certificate Signing Request (CSR) using generated keys
     * for the CSR public and private signgin key, to be used in the CSR
     * generation process.
     *
     * Generates and writes CSR out to the following paths in
     * both PEM and DER format (see variable values above):
     *     csrUsingGeneratedKeysDer (DER format)
     *     csrUsingGeneratedKeysPem (PEM format)
     *
     * @throws WolfSSLException if error occurs during CSR generation process.
     * @throws WolfSSLJNIException if native JNI error occurs
     * @throws IOException on error writing to output file locations
     */
    public void generateCSRUsingGeneratedKeys()
        throws WolfSSLException, WolfSSLJNIException, IOException,
               CertificateException, NoSuchAlgorithmException {

        System.out.print("\nGenerating CSR with generated keys");

        /* Create new CSR object */
        WolfSSLCertRequest req = new WolfSSLCertRequest();

        /* Set Subject Name */
        WolfSSLX509Name subjectName = generateTestSubjectName();
        req.setSubjectName(subjectName);

        /* Set Public Key from generated java.security.PublicKey */
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        KeyPair keyPair = kpg.generateKeyPair();
        PublicKey pubKey = keyPair.getPublic();
        req.setPublicKey(pubKey);

        /* Add Attributes */
        req.addAttribute(WolfSSL.NID_pkcs9_challengePassword,
                test_CHALLENGE_PASSWORD.getBytes());

        /* Add Extensions */
        req.addExtension(WolfSSL.NID_key_usage, test_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_ext_key_usage, test_EXT_KEY_USAGE, false);
        req.addExtension(WolfSSL.NID_subject_alt_name, test_ALT_NAME, false);
        req.addExtension(WolfSSL.NID_basic_constraints, true, true);

        /* Sign CSR, with java.security.PrivateKey */
        PrivateKey privKey = keyPair.getPrivate();
        req.signRequest(privKey, "SHA256");

        /* Output to DER and PEM files */
        byte[] derCsr = req.getDer();
        byte[] pemCsr = req.getPem();

        /* Write out generated CSRs to files */
        writeFile(csrUsingGeneratedKeysDer, derCsr);
        writeFile(csrUsingGeneratedKeysPem, pemCsr);

        System.out.println("... ");
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingGeneratedKeysDer).getFileName());
        System.out.println("    " + CERT_DIR_FROM_ROOT +
                Paths.get(csrUsingGeneratedKeysPem).getFileName());

        /* Free native memory */
        subjectName.free();
        req.free();
    }

    public void run(String[] args) {

        int ret = 0;

        try {
            /* Initialize and load native wolfSSL library, enable debugging */
            WolfSSL.loadLibrary();
            WolfSSL sslLib = new WolfSSL();

            /* Enable debugging if desired */
            //sslLib.debuggingON();

            System.out.println(
                "wolfSSL JNI Certificate Signing Request Generation Example");

            if (!WolfSSL.certReqEnabled()) {
                System.out.println("ERROR: Native wolfSSL must be compiled " +
                    "with --enable-certreq or WOLFSSL_CERT_REQ to use this " +
                    "example");

                /* exit with error */
                System.exit(1);
            }

            /* Generate example Certificate Signing Request files */
            generateCSRUsingFiles();
            generateCSRUsingArrays();
            generateCSRUsingGeneratedKeys();

        } catch (WolfSSLException | WolfSSLJNIException |
                 IOException | CertificateException |
                 NoSuchAlgorithmException e) {
            e.printStackTrace();

            /* exit with error */
            System.exit(1);
        }

    } /* end run() */

    public static void main(String[] args) {
        new X509CertRequest().run(args);
    }

} /* end X509CertRequest */


