diff --git a/IDE/WIN/README.md b/IDE/WIN/README.md index 33997b6..96a095b 100644 --- a/IDE/WIN/README.md +++ b/IDE/WIN/README.md @@ -419,9 +419,10 @@ to run the ProviderTest: examples\provider\ProviderTest.bat ``` -Or to run the X509v3 certificate generation example: +Or to run the X509v3 certificate or CSR generation example: ``` examples\X509v3CertificateGeneration.bat +examples\X509CertRequest.bat ``` diff --git a/IDE/WIN/wolfssljni.vcxproj b/IDE/WIN/wolfssljni.vcxproj index 68285ef..349e037 100644 --- a/IDE/WIN/wolfssljni.vcxproj +++ b/IDE/WIN/wolfssljni.vcxproj @@ -41,6 +41,7 @@ + @@ -54,6 +55,7 @@ + @@ -430,4 +432,4 @@ ant - \ No newline at end of file + diff --git a/IDE/WIN/wolfssljni.vcxproj.filters b/IDE/WIN/wolfssljni.vcxproj.filters index c330006..a0b5143 100644 --- a/IDE/WIN/wolfssljni.vcxproj.filters +++ b/IDE/WIN/wolfssljni.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + Source Files @@ -71,6 +74,9 @@ Header Files + + Header Files + Header Files @@ -84,4 +90,4 @@ Header Files - \ No newline at end of file + diff --git a/examples/README.md b/examples/README.md index 6ab988d..8e9e0b3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -63,6 +63,27 @@ This will write out generated certificates to the following directory: examples/certs/generated/ ``` +## Certificate Signing Request (CSR) Generation Example + +An example is included which will generate Certificate Signing Requests (CSR) +using the wolfSSL JNI library `WolfSSLCertRequest` class. + +**X509CertRequest.java** - CSR generation example + +This example is compiled when the `ant examples` target is executed, and can +be run afterwards with the provided bash script: + +``` +$ cd +$ ./examples/X509CertRequest.sh +``` + +This will write out generated CSRs to the following directory: + +``` +examples/certs/generated/ +``` + ## Support Please contact the wolfSSL support team at support@wolfssl.com with any diff --git a/examples/X509CertRequest.bat b/examples/X509CertRequest.bat new file mode 100644 index 0000000..3a3d4e3 --- /dev/null +++ b/examples/X509CertRequest.bat @@ -0,0 +1,14 @@ + +cd %~dp0\build >NUL 2>NUL +SETLOCAL + +:: Populate correct config for build +call ..\WindowsConfig.bat + +:: Set PATH to include DLL for native wolfSSL and wolfSSL JNI (native library) +SET PATH="%WOLFSSLJNI_DLL_DIR%;%WOLFSSL_DLL_DIR%";%PATH% + +java -cp ".;..\..\lib\wolfssl.jar;..\..\lib\wolfssl-jsse.jar" -Djava.library.path="%WOLFSSLJNI_DLL_DIR%;%WOLFSSL_DLL_DIR%" X509CertRequest + +ENDLOCAL +cd %~dp0\.. diff --git a/examples/X509CertRequest.java b/examples/X509CertRequest.java new file mode 100644 index 0000000..6bc9ec6 --- /dev/null +++ b/examples/X509CertRequest.java @@ -0,0 +1,366 @@ +/* X509CertRequest.java + * + * Copyright (C) 2006-2023 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 */ + + diff --git a/examples/X509CertRequest.sh b/examples/X509CertRequest.sh new file mode 100755 index 0000000..b9865d3 --- /dev/null +++ b/examples/X509CertRequest.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Allow user to override which openssl binary is used to verify certs +if [ -z "${OPENSSL}" ]; then + OPENSSL=openssl +fi + +cd ./examples/build +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../lib/:/usr/local/lib +java -classpath ../../lib/wolfssl.jar:./ -Dsun.boot.library.path=../../lib/ -Xcheck:jni X509CertRequest $@ + +if [ $? != 0 ]; then + printf "\nExample failed\n" + exit -1 +else + printf "\nExample passed\n" +fi + +which $OPENSSL > /dev/null +if [ $? != 0 ]; then + printf "openssl not detected, skipping cert verification\n" + exit -1 +fi + +printf "\nVerifying CSRs with openssl...\n" + +printf "Testing each can be opened with openssl req -text\n" + +# Test reading each DER CSR +CERT_FILES="../certs/generated/csr*.der" +for f in $CERT_FILES +do + $OPENSSL req -inform DER -in $f -text -noout > /dev/null + if [ $? != 0 ]; then + printf "File not readable with openssl req: $f\n" + exit -1 + fi +done + +# Test reading each PEM CSR +CERT_FILES="../certs/generated/csr*.pem" +for f in $CERT_FILES +do + $OPENSSL req -inform PEM -in $f -text -noout > /dev/null + if [ $? != 0 ]; then + printf "File not readable with openssl req: $f\n" + exit -1 + fi +done + +printf "Verification successful\n" diff --git a/examples/certs/ecc-client-key.der b/examples/certs/ecc-client-key.der new file mode 100644 index 0000000..0637390 Binary files /dev/null and b/examples/certs/ecc-client-key.der differ diff --git a/examples/certs/update-certs.sh b/examples/certs/update-certs.sh index c113aeb..3404765 100755 --- a/examples/certs/update-certs.sh +++ b/examples/certs/update-certs.sh @@ -41,6 +41,7 @@ certList=( "client-key.der" "client-keyPub.der" "dh2048.pem" + "ecc-client-key.der" "ecc-client-key.pem" "ecc-key.pem" "server-cert.pem" diff --git a/java.sh b/java.sh index cfbc258..3f2f4b4 100755 --- a/java.sh +++ b/java.sh @@ -115,10 +115,11 @@ gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_RSA.c -o ./native/com_ gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_ECC.c -o ./native/com_wolfssl_wolfcrypt_ECC.o $javaIncludes gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_EccKey.c -o ./native/com_wolfssl_wolfcrypt_EccKey.o $javaIncludes gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertManager.c -o ./native/com_wolfssl_WolfSSLCertManager.o $javaIncludes +gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertRequest.c -o ./native/com_wolfssl_WolfSSLCertRequest.o $javaIncludes gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertificate.c -o ./native/com_wolfssl_WolfSSLCertificate.o $javaIncludes gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLX509Name.c -o ./native/com_wolfssl_WolfSSLX509Name.o $javaIncludes gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLX509StoreCtx.c -o ./native/com_wolfssl_WolfSSLX509StoreCtx.o $javaIncludes -gcc -Wall $javaLibs $cflags -o ./lib/$jniLibName ./native/com_wolfssl_WolfSSL.o ./native/com_wolfssl_WolfSSLSession.o ./native/com_wolfssl_WolfSSLContext.o ./native/com_wolfssl_wolfcrypt_RSA.o ./native/com_wolfssl_wolfcrypt_ECC.o ./native/com_wolfssl_wolfcrypt_EccKey.o ./native/com_wolfssl_WolfSSLCertManager.o ./native/com_wolfssl_WolfSSLCertificate.o ./native/com_wolfssl_WolfSSLX509Name.o ./native/com_wolfssl_WolfSSLX509StoreCtx.o -L$WOLFSSL_INSTALL_DIR/lib -L$WOLFSSL_INSTALL_DIR/lib64 -l$WOLFSSL_LIBNAME +gcc -Wall $javaLibs $cflags -o ./lib/$jniLibName ./native/com_wolfssl_WolfSSL.o ./native/com_wolfssl_WolfSSLSession.o ./native/com_wolfssl_WolfSSLContext.o ./native/com_wolfssl_wolfcrypt_RSA.o ./native/com_wolfssl_wolfcrypt_ECC.o ./native/com_wolfssl_wolfcrypt_EccKey.o ./native/com_wolfssl_WolfSSLCertManager.o ./native/com_wolfssl_WolfSSLCertRequest.o ./native/com_wolfssl_WolfSSLCertificate.o ./native/com_wolfssl_WolfSSLX509Name.o ./native/com_wolfssl_WolfSSLX509StoreCtx.o -L$WOLFSSL_INSTALL_DIR/lib -L$WOLFSSL_INSTALL_DIR/lib64 -l$WOLFSSL_LIBNAME if [ $? != 0 ]; then echo "Error creating native JNI library" exit 1 diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index 3d315a2..15d31f0 100644 --- a/native/com_wolfssl_WolfSSL.c +++ b/native/com_wolfssl_WolfSSL.c @@ -386,6 +386,19 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_FileSystemEnabled #endif } +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_certReqEnabled + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef WOLFSSL_CERT_REQ + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_SSLv3_1ServerMethod (JNIEnv* jenv, jclass jcl) { diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index c4b71e3..b68a573 100644 --- a/native/com_wolfssl_WolfSSL.h +++ b/native/com_wolfssl_WolfSSL.h @@ -211,6 +211,20 @@ extern "C" { #define com_wolfssl_WolfSSL_ASN_URI_TYPE 6L #undef com_wolfssl_WolfSSL_ASN_IP_TYPE #define com_wolfssl_WolfSSL_ASN_IP_TYPE 7L +#undef com_wolfssl_WolfSSL_NID_surname +#define com_wolfssl_WolfSSL_NID_surname 4L +#undef com_wolfssl_WolfSSL_NID_serialNumber +#define com_wolfssl_WolfSSL_NID_serialNumber 5L +#undef com_wolfssl_WolfSSL_NID_pkcs9_unstructuredName +#define com_wolfssl_WolfSSL_NID_pkcs9_unstructuredName 49L +#undef com_wolfssl_WolfSSL_NID_pkcs9_contentType +#define com_wolfssl_WolfSSL_NID_pkcs9_contentType 50L +#undef com_wolfssl_WolfSSL_NID_pkcs9_challengePassword +#define com_wolfssl_WolfSSL_NID_pkcs9_challengePassword 54L +#undef com_wolfssl_WolfSSL_NID_givenName +#define com_wolfssl_WolfSSL_NID_givenName 100L +#undef com_wolfssl_WolfSSL_NID_initials +#define com_wolfssl_WolfSSL_NID_initials 101L #undef com_wolfssl_WolfSSL_NID_key_usage #define com_wolfssl_WolfSSL_NID_key_usage 129L #undef com_wolfssl_WolfSSL_NID_subject_alt_name @@ -219,6 +233,8 @@ extern "C" { #define com_wolfssl_WolfSSL_NID_basic_constraints 133L #undef com_wolfssl_WolfSSL_NID_ext_key_usage #define com_wolfssl_WolfSSL_NID_ext_key_usage 151L +#undef com_wolfssl_WolfSSL_NID_dnQualifier +#define com_wolfssl_WolfSSL_NID_dnQualifier 174L #undef com_wolfssl_WolfSSL_WOLFSSL_NAMED_GROUP_INVALID #define com_wolfssl_WolfSSL_WOLFSSL_NAMED_GROUP_INVALID 0L #undef com_wolfssl_WolfSSL_WOLFSSL_ECC_SECT163K1 @@ -477,6 +493,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_RsaEnabled JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_FileSystemEnabled (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: certReqEnabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_certReqEnabled + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: SSLv3_ServerMethod diff --git a/native/com_wolfssl_WolfSSLCertRequest.c b/native/com_wolfssl_WolfSSLCertRequest.c new file mode 100644 index 0000000..14d515f --- /dev/null +++ b/native/com_wolfssl_WolfSSLCertRequest.c @@ -0,0 +1,728 @@ +/* com_wolfssl_WolfSSLCertRequest.c + * + * Copyright (C) 2006-2023 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 + */ + +#include +#include + +#ifdef WOLFSSL_USER_SETTINGS + #include +#else + #include +#endif +#include +#include +#include +#include +#include /* for EVP_PKEY functions */ +#include /* for WOLFSSL_X509_EXTENSION */ +#include + +#include "com_wolfssl_globals.h" +#include "com_wolfssl_WolfSSLCertRequest.h" + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1new + (JNIEnv* jenv, jclass jcl) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = NULL; + (void)jcl; + + if (jenv == NULL) { + return (jlong)0; + } + + x509 = wolfSSL_X509_REQ_new(); + if (x509 == NULL) { + return (jlong)0; + } + + return (jlong)(uintptr_t)x509; +#else + (void)jenv; + (void)jcl; + return (jlong)0; +#endif +} + +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1free + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return; + } + + wolfSSL_X509_REQ_free(x509); +#else + (void)jenv; + (void)jcl; + return; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1subject_1name + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jlong x509NamePtr) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + WOLFSSL_X509_NAME* x509Name = (WOLFSSL_X509_NAME*)(uintptr_t)x509NamePtr; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || x509Name == NULL) { + return ret; + } + + ret = wolfSSL_X509_REQ_set_subject_name(x509, x509Name); + + return ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)x509NamePtr; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1add1_1attr_1by_1NID + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jint nid, jint type, jbyteArray attrBytes) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + int ret = WOLFSSL_SUCCESS; + unsigned char* attr = NULL; + int attrSz = 0; + (void)jcl; + + if (jenv == NULL || x509 == NULL || attrBytes == NULL) { + return WOLFSSL_FAILURE; + } + + attr = (byte*)(*jenv)->GetByteArrayElements(jenv, attrBytes, NULL); + attrSz = (*jenv)->GetArrayLength(jenv, attrBytes); + + if (attr == NULL || attrSz <= 0) { + ret = WOLFSSL_FAILURE; + } + + if (ret == WOLFSSL_SUCCESS) { + ret = X509_REQ_add1_attr_by_NID(x509, (int)nid, (int)type, + attr, attrSz); + } + + (*jenv)->ReleaseByteArrayElements(jenv, attrBytes, (jbyte*)attr, JNI_ABORT); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)nid; + (void)type; + (void)attrBytes; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1version + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jlong version) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + int ret = WOLFSSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + ret = X509_REQ_set_version(x509, (long)version); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)version; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1print + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(OPENSSL_EXTRA) && !defined(NO_BIO) && defined(XSNPRINTF) && \ + defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + WOLFSSL_BIO* bio = NULL; + char* mem = NULL; + int sz = 0; + jbyteArray memArr = NULL; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return NULL; + } + + bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); + if (bio == NULL) { + return NULL; + } + + if (wolfSSL_X509_REQ_print(bio, x509) != WOLFSSL_SUCCESS) { + wolfSSL_BIO_free(bio); + return NULL; + } + + sz = wolfSSL_BIO_get_mem_data(bio, &mem); + if (sz > 0 && mem != NULL) { + + memArr = (*jenv)->NewByteArray(jenv, sz); + if (memArr == NULL) { + wolfSSL_BIO_free(bio); + return NULL; + } + + (*jenv)->SetByteArrayRegion(jenv, memArr, 0, sz, (jbyte*)mem); + if ((*jenv)->ExceptionOccurred(jenv)) { + /* failed to set byte region */ + (*jenv)->DeleteLocalRef(jenv, memArr); + wolfSSL_BIO_free(bio); + return NULL; + } + } + wolfSSL_BIO_free(bio); + return memArr; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + return NULL; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jint keyType, jbyteArray keyBytes, jint fileFormat, jstring digestAlg) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + byte* keyBuf = NULL; + byte* derBuf = NULL; + int keySz = 0; + int derSz = 0; + byte derAllocated = 0; + WOLFSSL_EVP_PKEY* priv = NULL; + const WOLFSSL_EVP_MD* md = NULL; + unsigned char* rsaPrivBuf = NULL; + const char* mdName = NULL; + int ret = WOLFSSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + keyBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, keyBytes, NULL); + keySz = (*jenv)->GetArrayLength(jenv, keyBytes); + + if (keyBuf == NULL || keySz == 0) { + ret = WOLFSSL_FAILURE; + } + + /* Set correct WOLFSSL_EVP_MD, does not need to be freed */ + if (ret == WOLFSSL_SUCCESS) { + mdName = (*jenv)->GetStringUTFChars(jenv, digestAlg, 0); + if (mdName == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + md = wolfSSL_EVP_get_digestbyname(mdName); + if (md == NULL) { + ret = WOLFSSL_FAILURE; + } + } + } + + /* convert PEM to DER if needed */ + if (ret == WOLFSSL_SUCCESS) { + if ((int)fileFormat == WOLFSSL_FILETYPE_ASN1) { + /* already in DER */ + derBuf = keyBuf; + derSz = keySz; + } + else { + /* get needed buffer size */ + ret = wc_KeyPemToDer(keyBuf, keySz, NULL, 0, NULL); + if (ret <= 0) { + ret = WOLFSSL_FAILURE; + } + else { + derSz = ret; + derBuf = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (derBuf == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_SUCCESS; + derAllocated = 1; + XMEMSET(derBuf, 0, derSz); + } + } + } + } + + /* convert PEM to DER if derBuf has been allocated */ + if (derAllocated == 1 && ret == WOLFSSL_SUCCESS) { + ret = wc_KeyPemToDer(keyBuf, keySz, derBuf, derSz, NULL); + if (ret <= 0 || ret != derSz) { + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_SUCCESS; + } + } + + /* convert buffer into WOLFSSL_EVP_PKEY */ + if (ret == WOLFSSL_SUCCESS) { + rsaPrivBuf = derBuf; + + priv = wolfSSL_d2i_PrivateKey((int)keyType, NULL, + (const unsigned char**)&rsaPrivBuf, derSz); + if (priv == NULL) { + ret = WOLFSSL_FAILURE; + } + } + + /* sign WOLFSSL_X509 with WOLFSSL_EVP_PKEY, returns size of signature + * on success or negative on error */ + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_REQ_sign(x509, priv, md); + if (ret >= 0) { + ret = WOLFSSL_SUCCESS; + } + } + + if (priv != NULL) { + wolfSSL_EVP_PKEY_free(priv); + } + if (derAllocated == 1 && derBuf != NULL) { + XMEMSET(derBuf, 0, derSz); + XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + (*jenv)->ReleaseByteArrayElements(jenv, keyBytes, (jbyte*)keyBuf, + JNI_ABORT); + (*jenv)->ReleaseStringUTFChars(jenv, digestAlg, mdName); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)keyType; + (void)keyBytes; + (void)fileFormat; + (void)digestAlg; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1pubkey_1native_1open + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jint keyType, jbyteArray fileBytes, jint fileFormat) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + byte* fileBuf = NULL; + byte* derBuf = NULL; + int fileSz = 0; + int derSz = 0; + byte derAllocated = 0; + WOLFSSL_EVP_PKEY* pub = NULL; + unsigned char* rsaPubBuf = NULL; + int ret = WOLFSSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + fileBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, fileBytes, NULL); + fileSz = (*jenv)->GetArrayLength(jenv, fileBytes); + + if (fileBuf == NULL || fileSz == 0) { + ret = WOLFSSL_FAILURE; + } + + /* convert PEM to DER if needed */ + if (ret == WOLFSSL_SUCCESS) { + if ((int)fileFormat == WOLFSSL_FILETYPE_ASN1) { + /* already in DER */ + derBuf = fileBuf; + derSz = fileSz; + } + else { + /* get needed buffer size */ + ret = wc_KeyPemToDer(fileBuf, fileSz, NULL, 0, NULL); + if (ret <= 0) { + ret = WOLFSSL_FAILURE; + } + else { + derSz = ret; + derBuf = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (derBuf == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_SUCCESS; + derAllocated = 1; + XMEMSET(derBuf, 0, derSz); + } + } + } + } + + /* convert PEM to DER if derBuf has been allocated */ + if (derAllocated == 1 && ret == WOLFSSL_SUCCESS) { + ret = wc_KeyPemToDer(fileBuf, fileSz, derBuf, derSz, NULL); + if (ret <= 0 || ret != derSz) { + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_SUCCESS; + } + } + + /* convert buffer into WOLFSSL_EVP_PKEY */ + if (ret == WOLFSSL_SUCCESS) { + rsaPubBuf = derBuf; + + pub = wolfSSL_d2i_PUBKEY(NULL, (const unsigned char**)&rsaPubBuf, derSz); + if (pub == NULL) { + ret = WOLFSSL_FAILURE; + } + } + + /* set WOLFSSL_EVP_PKEY into WOLFSSL_X509 */ + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_set_pubkey(x509, pub); + } + + if (pub != NULL) { + /* free WOLFSSL_EVP_PKEY, since X509_set_pubkey() makes copy */ + wolfSSL_EVP_PKEY_free(pub); + } + if (derAllocated == 1 && derBuf != NULL) { + XMEMSET(derBuf, 0, derSz); + XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + (*jenv)->ReleaseByteArrayElements(jenv, fileBytes, (jbyte*)fileBuf, + JNI_ABORT); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)keyType; + (void)fileBytes; + (void)fileFormat; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1get_1der + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) && \ + !defined(NO_BIO) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + unsigned char* der = NULL; + jbyteArray derArr = NULL; + jclass excClass = NULL; + int sz = 0; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return NULL; + } + + sz = wolfSSL_i2d_X509_REQ(x509, &der); + if (sz <= 0) { + return NULL; + } + + derArr = (*jenv)->NewByteArray(jenv, sz); + if (derArr == NULL) { + (*jenv)->ThrowNew(jenv, jcl, + "Failed to create byte array in native X509_REQ_get_der"); + return NULL; + } + + excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, derArr); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + return NULL; + } + + (*jenv)->SetByteArrayRegion(jenv, derArr, 0, sz, (jbyte*)der); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, derArr); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + (*jenv)->ThrowNew(jenv, excClass, + "Failed to set byte region in native X509_REQ_get_der"); + return NULL; + } + + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + + return derArr; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + return NULL; +#endif +} + +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1get_1pem + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) && \ + !defined(NO_BIO) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + unsigned char* der = NULL; + unsigned char* pem = NULL; + int sz = 0; + int pemSz = 0; + jbyteArray pemArr = NULL; + jclass excClass = NULL; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return NULL; + } + + sz = wolfSSL_i2d_X509_REQ(x509, &der); + if (sz <= 0) { + return NULL; + } + + pemSz = wc_DerToPem(der, sz, NULL, 0, CERTREQ_TYPE); + if (pemSz < 0) { + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + return NULL; + } + + pem = (byte*)XMALLOC(pemSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (pem == NULL) { + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + return NULL; + } + XMEMSET(pem, 0, pemSz); + + pemSz = wc_DerToPem(der, sz, pem, pemSz, CERTREQ_TYPE); + if (pemSz < 0) { + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + + pemArr = (*jenv)->NewByteArray(jenv, pemSz); + if (pemArr == NULL) { + (*jenv)->ThrowNew(jenv, jcl, + "Failed to create byte array in native X509_REQ_get_pem"); + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, pemArr); + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + (*jenv)->SetByteArrayRegion(jenv, pemArr, 0, pemSz, (jbyte*)pem); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, pemArr); + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + (*jenv)->ThrowNew(jenv, excClass, + "Failed to set byte region in native X509_get_pem"); + return NULL; + } + + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return pemArr; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + return NULL; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1add_1ext_1via_1nconf_1nid + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jint nid, jstring extValue, jboolean isCritical) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + WOLFSSL_X509_EXTENSION* ext = NULL; + const char* value = NULL; + int ret = WOLFSSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + value = (*jenv)->GetStringUTFChars(jenv, extValue, 0); + if (value == NULL) { + ret = WOLFSSL_FAILURE; + } + + if (ret == WOLFSSL_SUCCESS) { + ext = wolfSSL_X509V3_EXT_nconf_nid(NULL, NULL, (int)nid, value); + if (ext == NULL) { + ret = WOLFSSL_FAILURE; + } + } + + if (ret == WOLFSSL_SUCCESS) { + if (isCritical == JNI_TRUE) { + ret = wolfSSL_X509_EXTENSION_set_critical(ext, 1); + } + } + + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_add_ext(x509, ext, -1); + } + + if (ext != NULL) { + wolfSSL_X509_EXTENSION_free(ext); + } + + (*jenv)->ReleaseStringUTFChars(jenv, extValue, value); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)nid; + (void)extValue; + (void)isCritical; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1add_1ext_1via_1set_1object_1boolean + (JNIEnv* jenv, jclass jcl, jlong x509ReqPtr, jint nid, jboolean extValue, jboolean isCritical) +{ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509ReqPtr; + WOLFSSL_X509_EXTENSION* ext = NULL; + WOLFSSL_ASN1_OBJECT* obj = NULL; + int ret = WOLFSSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + ext = wolfSSL_X509_EXTENSION_new(); + if (ext == NULL) { + ret = WOLFSSL_FAILURE; + } + + if (ret == WOLFSSL_SUCCESS) { + if (isCritical == JNI_TRUE) { + ret = wolfSSL_X509_EXTENSION_set_critical(ext, 1); + } + } + + if (ret == WOLFSSL_SUCCESS) { + obj = wolfSSL_OBJ_nid2obj((int)nid); + if (obj == NULL) { + ret = WOLFSSL_FAILURE; + } + } + + if (ret == WOLFSSL_SUCCESS) { + if (extValue == JNI_TRUE) { + obj->ca = 1; + } + else { + obj->ca = 0; + } + } + + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_EXTENSION_set_object(ext, obj); + } + + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_add_ext(x509, ext, -1); + } + + + if (obj != NULL) { + wolfSSL_ASN1_OBJECT_free(obj); + } + if (ext != NULL) { + wolfSSL_X509_EXTENSION_free(ext); + } + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509ReqPtr; + (void)nid; + (void)extValue; + (void)isCritical; + return (jint)NOT_COMPILED_IN; +#endif +} + diff --git a/native/com_wolfssl_WolfSSLCertRequest.h b/native/com_wolfssl_WolfSSLCertRequest.h new file mode 100644 index 0000000..88900c7 --- /dev/null +++ b/native/com_wolfssl_WolfSSLCertRequest.h @@ -0,0 +1,115 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_wolfssl_WolfSSLCertRequest */ + +#ifndef _Included_com_wolfssl_WolfSSLCertRequest +#define _Included_com_wolfssl_WolfSSLCertRequest +#ifdef __cplusplus +extern "C" { +#endif +#undef com_wolfssl_WolfSSLCertRequest_EVP_PKEY_RSA +#define com_wolfssl_WolfSSLCertRequest_EVP_PKEY_RSA 16L +#undef com_wolfssl_WolfSSLCertRequest_EVP_PKEY_EC +#define com_wolfssl_WolfSSLCertRequest_EVP_PKEY_EC 18L +#undef com_wolfssl_WolfSSLCertRequest_MBSTRING_ASC +#define com_wolfssl_WolfSSLCertRequest_MBSTRING_ASC 4097L +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_new + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1new + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_free + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1free + (JNIEnv *, jclass, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_set_subject_name + * Signature: (JJ)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1subject_1name + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_add1_attr_by_NID + * Signature: (JII[B)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1add1_1attr_1by_1NID + (JNIEnv *, jclass, jlong, jint, jint, jbyteArray); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_set_version + * Signature: (JJ)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1version + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_print + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1print + (JNIEnv *, jclass, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_sign + * Signature: (JI[BILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign + (JNIEnv *, jclass, jlong, jint, jbyteArray, jint, jstring); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_set_pubkey_native_open + * Signature: (JI[BI)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1set_1pubkey_1native_1open + (JNIEnv *, jclass, jlong, jint, jbyteArray, jint); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_get_der + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1get_1der + (JNIEnv *, jclass, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_REQ_get_pem + * Signature: (J)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1get_1pem + (JNIEnv *, jclass, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_add_ext_via_nconf_nid + * Signature: (JILjava/lang/String;Z)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1add_1ext_1via_1nconf_1nid + (JNIEnv *, jclass, jlong, jint, jstring, jboolean); + +/* + * Class: com_wolfssl_WolfSSLCertRequest + * Method: X509_add_ext_via_set_object_boolean + * Signature: (JIZZ)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1add_1ext_1via_1set_1object_1boolean + (JNIEnv *, jclass, jlong, jint, jboolean, jboolean); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index 5b1fca2..d16b284 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -361,14 +361,30 @@ public class WolfSSL { public static final int ASN_IP_TYPE = 0x07; /* NIDs, from native asn.h */ + /** Surname NID */ + public static final int NID_surname = 4; + /** Serial number NID */ + public static final int NID_serialNumber = 5; + /** PKCS9 Unstructured name NID */ + public static final int NID_pkcs9_unstructuredName = 49; + /** PKCS9 contentType NID */ + public static final int NID_pkcs9_contentType = 50; + /** PKCS9 challenge password NID */ + public static final int NID_pkcs9_challengePassword = 54; + /** Given name NID */ + public static final int NID_givenName = 100; + /** Initials NID */ + public static final int NID_initials = 101; /** Key Usage NID */ - public static final int NID_key_usage = 129; + public static final int NID_key_usage = 129; /** Subject Alternative Name NID */ - public static final int NID_subject_alt_name = 131; + public static final int NID_subject_alt_name = 131; /** Basic Constraints NID */ - public static final int NID_basic_constraints = 133; + public static final int NID_basic_constraints = 133; /** Extended Key Usage NID */ - public static final int NID_ext_key_usage = 151; + public static final int NID_ext_key_usage = 151; + /** Domain name qualifier NID */ + public static final int NID_dnQualifier = 174; /* is this object active, or has it been cleaned up? */ private boolean active = false; @@ -590,52 +606,61 @@ public class WolfSSL { * TLS 1.0 is disabled by default in native wolfSSL, unless the user * has configured wolfSSL with "--enable-tls10". * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean TLSv1Enabled(); /** * Tests if TLS 1.1 has been compiled into the native wolfSSL library. * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean TLSv11Enabled(); /** * Tests if TLS 1.2 has been compiled into the native wolfSSL library. * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean TLSv12Enabled(); /** * Tests if TLS 1.3 has been compiled into the native wolfSSL library. * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean TLSv13Enabled(); /** * Tests if ECC support has been compiled into the native wolfSSL library. * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean EccEnabled(); /** * Tests if RSA support has been compiled into the native wolfSSL library. * - * @return 1 if enabled, otherwise 0 if not compiled in. + * @return true if enabled, otherwise false if not compiled in. */ public static native boolean RsaEnabled(); /** * Tests if filesystem support has been compiled into the wolfSSL library. * - * @return 1 if enabled, otherwise 0 if NO_FILESYSTEM has been defined. + * @return true if enabled, otherwise false if NO_FILESYSTEM has been + * defined. */ public static native boolean FileSystemEnabled(); + /** + * Tests if Certificate Signing Request (CSR) support has been compiled + * into the native wolfSSL library. + * + * @return true if enabled, otherwise false if WOLFSSL_CERT_EXT not defined. + */ + public static native boolean certReqEnabled(); + /* ---------------- native SSL/TLS version functions ---------------- */ /** diff --git a/src/java/com/wolfssl/WolfSSLCertRequest.java b/src/java/com/wolfssl/WolfSSLCertRequest.java new file mode 100644 index 0000000..78093d8 --- /dev/null +++ b/src/java/com/wolfssl/WolfSSLCertRequest.java @@ -0,0 +1,730 @@ +/* WolfSSLCertRequest.java + * + * Copyright (C) 2006-2023 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 + */ +package com.wolfssl; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.charset.Charset; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.ECPrivateKey; + +/** + * WolfSSLCertRequest class, wraps native X509_REQ functionality. + */ +public class WolfSSLCertRequest { + + private boolean active = false; + + /* native X509_REQ pointer */ + private long x509ReqPtr = 0; + + /* lock around active state */ + private final Object stateLock = new Object(); + + /* lock around native X509_REQ pointer use */ + private final Object x509ReqLock = new Object(); + + /* Public key types used for CSR, mirrored from + * native enum in wolfssl/openssl/evp.h */ + private static final int EVP_PKEY_RSA = 16; + private static final int EVP_PKEY_EC = 18; + + /* Define from */ + private static final int MBSTRING_ASC = 0x1001; + + /* Native JNI methods */ + static native long X509_REQ_new(); + static native void X509_REQ_free(long x509ReqPtr); + static native int X509_REQ_set_subject_name(long x509ReqPtr, + long x509NamePtr); + static native int X509_REQ_add1_attr_by_NID(long x509ReqPtr, int nid, + int type, byte[] bytes); + static native int X509_REQ_set_version(long x509ReqPtr, long ver); + static native byte[] X509_REQ_print(long x509ReqPtr); + static native int X509_REQ_sign(long x509ReqPtr, int evpKeyType, + byte[] keyBytes, int format, String digestAlg); + static native int X509_REQ_set_pubkey_native_open(long x509ReqPtr, + int keyType, byte[] fileBytes, int format); + static native byte[] X509_REQ_get_der(long x509); + static native byte[] X509_REQ_get_pem(long x509); + static native int X509_add_ext_via_nconf_nid(long x509Ptr, int nid, + String extValue, boolean isCritical); + static native int X509_add_ext_via_set_object_boolean(long x509Ptr, + int nid, boolean extValue, boolean isCritical); + + /** + * Create new empty WolfSSLCertRequest object, for use with CSR generation + * + * @throws WolfSSLException if native API call fails. + */ + public WolfSSLCertRequest() throws WolfSSLException { + + x509ReqPtr = X509_REQ_new(); + if (x509ReqPtr == 0) { + throw new WolfSSLException("Failed to create WolfSSLCertRequest"); + } + + synchronized (stateLock) { + this.active = true; + } + } + + /** + * Verifies that the current WolfSSLCertRequest object is active. + * + * @throws IllegalStateException if object has been freed + */ + private void confirmObjectIsActive() + throws IllegalStateException { + + synchronized (stateLock) { + if (this.active == false) { + throw new IllegalStateException( + "WolfSSLCertRequest object has been freed"); + } + } + } + + /** + * Set the Subject Name to be used with this WolfSSLCertRequest. + * Note that the WolfSSLX509Name object should be completely set up + * before calling this method. This method copies/duplicates the contents + * of the WOLFSSL_X509_NAME (WolfSSLX509Name) into the native + * WOLFSSL_X509 structure. + * + * @param name Initialized and populated WolfSSLX509 name to be set into + * Subject Name of WolfSSLCertRequest for cert generation. + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if native JNI error occurs. + */ + public void setSubjectName(WolfSSLX509Name name) + throws IllegalStateException, WolfSSLException { + + int ret; + + confirmObjectIsActive(); + + synchronized (x509ReqLock) { + /* TODO somehow lock WolfSSLX509Name object while using pointer? */ + ret = X509_REQ_set_subject_name(this.x509ReqPtr, + name.getNativeX509NamePtr()); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException("Error setting subject name " + + "(ret: " + ret + ")"); + } + } + + /** + * Add a CSR attribute to this WolfSSLCertRequest + * + * @param nid NID of an attribute to add. Must be one of: + * WolfSSL.NID_pkcs9_challengePassword + * WolfSSL.NID_serialNumber + * WolfSSL.NID_pkcs9_unstructuredName + * WolfSSL.NID_pkcs9_contentType + * WolfSSL.NID_surname + * WolfSSL.NID_initials + * WolfSSL.NID_givenName + * WolfSSL.NID_dnQualifier + * @param value value of attribute to set, if passing in String, use + * String.getBytes() + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if native JNI error occurs. + */ + public void addAttribute(int nid, byte[] value) + throws IllegalStateException, WolfSSLException { + + int ret; + + confirmObjectIsActive(); + + if (nid != WolfSSL.NID_pkcs9_challengePassword && + nid != WolfSSL.NID_serialNumber && + nid != WolfSSL.NID_pkcs9_unstructuredName && + nid != WolfSSL.NID_pkcs9_contentType && + nid != WolfSSL.NID_surname && + nid != WolfSSL.NID_initials && + nid != WolfSSL.NID_givenName && + nid != WolfSSL.NID_dnQualifier) { + throw new WolfSSLException( + "Unsupported CSR attribute NID: " + nid); + } + + if (value == null || value.length == 0) { + throw new WolfSSLException( + "CSR attribute value may not be null or zero length"); + } + + synchronized (x509ReqLock) { + ret = X509_REQ_add1_attr_by_NID(this.x509ReqPtr, nid, + MBSTRING_ASC, value); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException("Error setting CSR attribute " + + "(ret: " + ret + ")"); + } + } + + /** + * Set CSR version for this WolfSSLCertRequest object. + * + * Calling this method is optional when generating a CSR. By default, + * a value of 0 (zero) is used for the CSR version. This is currently + * the version used by all CSR RFCs/specs. + * + * @param version version to set for CSR + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if native JNI error occurs. + */ + public void setVersion(long version) + throws IllegalStateException, WolfSSLException { + + int ret; + + confirmObjectIsActive(); + + synchronized (x509ReqLock) { + ret = X509_REQ_set_version(this.x509ReqPtr, version); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException("Error setting CSR version " + + "(ret: " + ret + ")"); + } + } + + /** + * Set public key for this WolfSSLCertRequest, used when generating + * Certificate Signing Requests + * + * @param filePath Path to public key file + * @param keyType Type of public key algorithm, options are: + * WolfSSL.RSAk + * WolfSSL.ECDSAk + * @param format Format of public key file, options are: + * WolfSSL.SSL_FILETYPE_ASN1 (DER formatted) + * WolfSSL.SSL_FILETYPE_PEM (PEM formatted) + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws IOException on error opening input file + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setPublicKey(String filePath, int keyType, int format) + throws IllegalStateException, IOException, WolfSSLException { + + int ret = 0; + File keyFile = null; + + confirmObjectIsActive(); + + if (filePath == null || filePath.isEmpty()) { + throw new WolfSSLException("File path is null or empty"); + } + + keyFile = new File(filePath); + if (!keyFile.exists()) { + throw new WolfSSLException("Input file does not exist: " + + filePath); + } + + setPublicKey(Files.readAllBytes(keyFile.toPath()), keyType, format); + } + + /** + * Set public key for this WolfSSLCertRequest, used when generating + * Certificate Signing Requests + * + * @param key Byte array containing public key + * @param keyType Type of public key algorithm, options are: + * WolfSSL.RSAk + * WolfSSL.ECDSAk + * @param format Format of public key file, options are: + * WolfSSL.SSL_FILETYPE_ASN1 (DER formatted) + * WolfSSL.SSL_FILETYPE_PEM (PEM formatted) + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws IOException on error opening input file + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setPublicKey(byte[] key, int keyType, int format) + throws IllegalStateException, IOException, WolfSSLException { + + int ret = 0; + int evpKeyType; + + confirmObjectIsActive(); + + if (key == null || key.length == 0) { + throw new WolfSSLException("Key array is null or empty"); + } + + if (format != WolfSSL.SSL_FILETYPE_ASN1 && + format != WolfSSL.SSL_FILETYPE_PEM) { + throw new WolfSSLException( + "Invalid key format, must be PEM or DER"); + } + + switch (keyType) { + case WolfSSL.RSAk: + evpKeyType = EVP_PKEY_RSA; + break; + case WolfSSL.ECDSAk: + evpKeyType = EVP_PKEY_EC; + break; + default: + throw new WolfSSLException("Unsupported public key type"); + } + + synchronized (x509ReqLock) { + ret = X509_REQ_set_pubkey_native_open(this.x509ReqPtr, evpKeyType, + key, format); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting public key into native WOLFSSL_X509_REQ " + + "(ret: " + ret + ")"); + } + } + + /** + * Set public key for this WolfSSLCertRequest, used when generating + * Certificate Signing Requests + * + * @param key PublicKey object containing public key to be used when + * generating CSR. + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + * @throws IOException on error opening/reading public key + */ + public void setPublicKey(PublicKey key) + throws IllegalStateException, IOException, WolfSSLException { + + int keyType; + byte[] encodedKey = null; + + confirmObjectIsActive(); + + if (key instanceof RSAPublicKey) { + keyType = WolfSSL.RSAk; + } + else if (key instanceof ECPublicKey) { + keyType = WolfSSL.ECDSAk; + } + else { + throw new WolfSSLException( + "PublicKey must be of type RSAPublicKey or ECPublicKey"); + } + + /* Get DER encoded key */ + encodedKey = key.getEncoded(); + if (encodedKey == null) { + throw new WolfSSLException( + "Error getting encoded (DER) format of PublicKey"); + } + + setPublicKey(encodedKey, keyType, WolfSSL.SSL_FILETYPE_ASN1); + } + + /** + * Add an extension to a WolfSSLCertRequest given the NID and extension + * value String. + * + * This method supports the following extensions: + * - Key Usage (WolfSSL.NID_key_usage) + * - Extended Key Usage (WolfSSL.NID_ext_key_usage) + * - Subject Alt Name (WolfSSL.NED_subject_alt_name) + * + * @param nid NID of extension to add. Must be one of: + * WolfSSL.NID_key_usage + * WolfSSL.NID_ext_key_usage + * WolfSSL.NID_subject_alt_name + * @param value String value of extension to set. For keyUsage and + * extKeyUsage this should be a comma-delimited list. + * For subjectAltName, this is a single value. Possible + * values for keyUsage and extKeyUsage are: + * + * NID_key_usage: + * digitalSignature + * nonRepudiation + * contentCommitment + * keyEncipherment + * dataEncipherment + * keyAgreement + * keyCertSign + * cRLSign + * encipherOnly + * decipherOnly + * + * NID_ext_key_usage: + * serverAuth + * clientAuth + * codeSigning + * emailProtection + * timeStamping + * OCSPSigning + * + * @param isCritical Boolean flag indicating if this extension is + * critical + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void addExtension(int nid, String value, boolean isCritical) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (nid != WolfSSL.NID_key_usage && + nid != WolfSSL.NID_subject_alt_name && + nid != WolfSSL.NID_ext_key_usage) { + throw new WolfSSLException( + "Unsupported X509v3 extension NID: " + nid); + } + + synchronized (x509ReqLock) { + ret = X509_add_ext_via_nconf_nid(this.x509ReqPtr, nid, value, + isCritical); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + if ((WolfSSL.getLibVersionHex() <= 0x05006003) && + (nid == WolfSSL.NID_key_usage || + nid == WolfSSL.NID_ext_key_usage)) { + + /* wolfSSL versions 5.6.3 and earlier did not include code + * fixes to native wolfSSL allowing this extension support to + * work. Use a version > 5.6.3 or apply patch from wolfSSL + * PR 6585 for correct support */ + throw new WolfSSLException( + "Error setting extension into native WOLFSSL_X509 " + + "(ret: " + ret + ").\nNeed to use wolfSSL version " + + "greater than 5.6.3 for extension support (PR 6585)."); + } + + throw new WolfSSLException( + "Error setting extension into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Add an extension to a WolfSSLCertRequest given the NID and extension + * value true/false value. + * + * This method supports the following extensions: + * - Basic Constraints (WolfSSL.NID_basic_constraints) + * + * @param nid NID of extension to add. Must be one of: + * WolfSSL.NID_basic_constraints + * @param value Boolean value of extension (true/false) + * @param isCritical Boolean flag indicating if this extension is + * critical + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void addExtension(int nid, boolean value, boolean isCritical) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (nid != WolfSSL.NID_basic_constraints) { + throw new WolfSSLException( + "Unsupported X509v3 extension NID: " + nid); + } + + synchronized (x509ReqLock) { + ret = X509_add_ext_via_set_object_boolean( + this.x509ReqPtr, nid, value, isCritical); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting extension into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Sign certificate request with private key from file. + * + * @param filePath Path to private key file + * @param keyType Type of public key algorithm, options are: + * WolfSSL.RSAk + * WolfSSL.ECDSAk + * @param format Format of private key file, options are: + * WolfSSL.SSL_FILETYPE_ASN1 (DER formatted) + * WolfSSL.SSL_FILETYPE_PEM (PEM formatted) + * @param digestAlg Message digest algorithm to use for signature + * generation. Options include the following, but native algorithm + * must be compiled into wolfSSL to be available: + * "MD4", "MD5", "SHA1", "SHA224", "SHA256", "SHA384", + * "SHA512", "SHA3_224", "SHA3_256", "SHA3_384", "SHA3_512" + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws IOException on error opening input file + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void signRequest(String filePath, int keyType, int format, + String digestAlg) throws IllegalStateException, IOException, + WolfSSLException { + + int ret = 0; + File keyFile = null; + + confirmObjectIsActive(); + + if (filePath == null || filePath.isEmpty()) { + throw new WolfSSLException("File path is null or empty"); + } + + keyFile = new File(filePath); + if (!keyFile.exists()) { + throw new WolfSSLException("Input file does not exist: " + + filePath); + } + + signRequest(Files.readAllBytes(keyFile.toPath()), keyType, + format, digestAlg); + } + + /** + * Sign certificate request with private key from buffer. + * + * @param key Byte array containing private key + * @param keyType Type of public key algorithm, options are: + * WolfSSL.RSAk + * WolfSSL.ECDSAk + * @param format Format of private key file, options are: + * WolfSSL.SSL_FILETYPE_ASN1 (DER formatted) + * WolfSSL.SSL_FILETYPE_PEM (PEM formatted) + * @param digestAlg Message digest algorithm to use for signature + * generation. Options include the following, but native algorithm + * must be compiled into wolfSSL to be available: + * "MD4", "MD5", "SHA1", "SHA224", "SHA256", "SHA384", + * "SHA512", "SHA3_224", "SHA3_256", "SHA3_384", "SHA3_512" + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void signRequest(byte[] key, int keyType, int format, + String digestAlg) throws IllegalStateException, WolfSSLException { + + int ret = 0; + int evpKeyType; + + confirmObjectIsActive(); + + if (key == null || key.length == 0) { + throw new WolfSSLException("Key array is null or empty"); + } + + if (format != WolfSSL.SSL_FILETYPE_ASN1 && + format != WolfSSL.SSL_FILETYPE_PEM) { + throw new WolfSSLException( + "Invalid key format, must be PEM or DER"); + } + + switch (keyType) { + case WolfSSL.RSAk: + evpKeyType = EVP_PKEY_RSA; + break; + case WolfSSL.ECDSAk: + evpKeyType = EVP_PKEY_EC; + break; + default: + throw new WolfSSLException("Unsupported private key type"); + } + + synchronized (x509ReqLock) { + ret = X509_REQ_sign(this.x509ReqPtr, evpKeyType, key, format, + digestAlg); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error signing native X509_REQ " + + "(ret: " + ret + ")"); + } + } + + /** + * Sign certificate request with private key from PrivateKey object. + * + * @param key java.security.PrivateKey object containing private key, + * must be of type RSAPrivateKey or ECPrivateKey + * @param digestAlg Message digest algorithm to use for signature + * generation. Options include the following, but native algorithm + * must be compiled into wolfSSL to be available: + * "MD4", "MD5", "SHA1", "SHA224", "SHA256", "SHA384", + * "SHA512", "SHA3_224", "SHA3_256", "SHA3_384", "SHA3_512" + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void signRequest(PrivateKey key, String digestAlg) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + int evpKeyType; + byte[] encodedKey = null; + + confirmObjectIsActive(); + + if (key == null) { + throw new WolfSSLException("Key object is null"); + } + + if (key instanceof RSAPrivateKey) { + evpKeyType = EVP_PKEY_RSA; + } + else if (key instanceof ECPrivateKey) { + evpKeyType = EVP_PKEY_EC; + } + else { + throw new WolfSSLException( + "PrivateKey must be of type RSAPrivateKey or ECPrivateKey"); + } + + /* Get DER encoded key */ + encodedKey = key.getEncoded(); + if (encodedKey == null) { + throw new WolfSSLException("PrivateKey does not support encoding"); + } + + synchronized (x509ReqLock) { + ret = X509_REQ_sign(this.x509ReqPtr, evpKeyType, encodedKey, + WolfSSL.SSL_FILETYPE_ASN1, digestAlg); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error signing native X509_REQ " + + "(ret: " + ret + ")"); + } + } + + /** + * Get ASN.1/DER encoding of this CSR, after signRequest() has been called. + * + * @return DER encoded array of CSR or null if not available. + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLJNIException if native JNI operation fails + */ + public byte[] getDer() throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (x509ReqLock) { + return X509_REQ_get_der(this.x509ReqPtr); + } + } + + /** + * Get PEM encoding of this CSR, after signRequest() has been called. + * + * @return PEM encoded array of CSR or null if not available. + * + * @throws IllegalStateException if WolfSSLCertRequest has been freed. + * @throws WolfSSLJNIException if native JNI operation fails + */ + public byte[] getPem() throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (x509ReqLock) { + return X509_REQ_get_pem(this.x509ReqPtr); + } + } + + @Override + public String toString() { + + byte[] x509ReqText = null; + + synchronized (stateLock) { + if (this.active == false) { + return super.toString(); + } + + synchronized (x509ReqLock) { + x509ReqText = X509_REQ_print(this.x509ReqPtr); + } + if (x509ReqText != null) { + /* let Java do the modified UTF-8 conversion */ + return new String(x509ReqText, Charset.forName("UTF-8")); + } else { + System.out.println("toString: x509ReqTest == null"); + } + } + + return super.toString(); + } + + /** + * Frees WolfSSLCertRequest native resources. + */ + public synchronized void free() { + + synchronized (stateLock) { + if (this.active == false) { + /* already freed, just return */ + return; + } + + synchronized (x509ReqLock) { + /* free native resources */ + X509_REQ_free(this.x509ReqPtr); + + /* free Java resources */ + this.active = false; + this.x509ReqPtr = 0; + } + } + } + + @SuppressWarnings("deprecation") + @Override + protected void finalize() throws Throwable + { + this.free(); + super.finalize(); + } +} + diff --git a/src/java/com/wolfssl/WolfSSLCertificate.java b/src/java/com/wolfssl/WolfSSLCertificate.java index e0aab09..d839b41 100644 --- a/src/java/com/wolfssl/WolfSSLCertificate.java +++ b/src/java/com/wolfssl/WolfSSLCertificate.java @@ -730,7 +730,32 @@ public class WolfSSLCertificate { * @param nid NID of extension to add. Must be one of: * WolfSSL.NID_key_usage * WolfSSL.NID_subject_alt_name - * @param value String value of extension to set + * WolfSSL.NID_subject_alt_name + * @param value String value of extension to set. For keyUsage and + * extKeyUsage this should be a comma-delimited list. + * For subjectAltName, this is a single value. Possible + * values for keyUsage and extKeyUsage are: + * + * NID_key_usage: + * digitalSignature + * nonRepudiation + * contentCommitment + * keyEncipherment + * dataEncipherment + * keyAgreement + * keyCertSign + * cRLSign + * encipherOnly + * decipherOnly + * + * NID_ext_key_usage: + * serverAuth + * clientAuth + * codeSigning + * emailProtection + * timeStamping + * OCSPSigning + * * @param isCritical Boolean flag indicating if this extension is * critical * @@ -785,8 +810,7 @@ public class WolfSSLCertificate { * - Basic Constraints (WolfSSL.NID_basic_constraints) * * @param nid NID of extension to add. Must be one of: - * WolfSSL.NID_key_usage - * WolfSSL.NID_subject_alt_name + * WolfSSL.NID_basic_constraints * @param value Boolean value of extension (true/false) * @param isCritical Boolean flag indicating if this extension is * critical diff --git a/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java new file mode 100644 index 0000000..d7c80b3 --- /dev/null +++ b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java @@ -0,0 +1,608 @@ +/* WolfSSLCertRequestTest.java + * + * Copyright (C) 2006-2023 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 + */ + +package com.wolfssl.test; + +import org.junit.Test; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import static org.junit.Assert.*; + +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.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import com.wolfssl.WolfSSL; +import com.wolfssl.WolfSSLX509Name; +import com.wolfssl.WolfSSLCertRequest; +import com.wolfssl.WolfSSLException; +import com.wolfssl.WolfSSLJNIException; + +/** + * @author wolfSSL + */ +public class WolfSSLCertRequestTest { + public final static int TEST_FAIL = -1; + public final static int TEST_SUCCESS = 0; + + public static String cliKeyDer = "examples/certs/client-key.der"; + public static String cliKeyPem = "examples/certs/client-key.pem"; + public static String cliKeyPubDer = "examples/certs/client-keyPub.der"; + public static String cliEccKeyDer = "examples/certs/ecc-client-key.der"; + public static String cliEccKeyPem = "examples/certs/ecc-client-key.pem"; + + @BeforeClass + public static void setCertPaths() throws WolfSSLException { + + System.out.println("WolfSSLCertRequest Class"); + + try { + WolfSSL.loadLibrary(); + } catch (UnsatisfiedLinkError ule) { + fail("failed to load native JNI library"); + } + + cliKeyDer = WolfSSLTestCommon.getPath(cliKeyDer); + cliKeyPubDer = WolfSSLTestCommon.getPath(cliKeyPubDer); + } + + /* Internal helper method, generate test SubjectName for cert generation */ + private WolfSSLX509Name GenerateTestSubjectName() throws WolfSSLException { + + WolfSSLX509Name name = new WolfSSLX509Name(); + + name.setCountryName("US"); + name.setStateOrProvinceName("Montana"); + name.setStreetAddress("12345 Test Address"); + name.setLocalityName("Bozeman"); + name.setSurname("Test Surname"); + name.setCommonName("wolfssl.com"); + name.setEmailAddress("support@wolfssl.com"); + name.setOrganizationName("wolfSSL Inc."); + name.setOrganizationalUnitName("Development Test"); + name.setUserId("TestUserID"); + + return name; + } + + @Test + public void testAddAttribute() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\taddAttribute()"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* Test adding supported attributes by NID */ + req.addAttribute(WolfSSL.NID_pkcs9_challengePassword, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_serialNumber, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_pkcs9_unstructuredName, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_pkcs9_contentType, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_surname, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_initials, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_givenName, + "12345".getBytes()); + req.addAttribute(WolfSSL.NID_dnQualifier, + "12345".getBytes()); + + /* Adding unsupported NID should throw exception */ + try { + req.addAttribute(123456, + "12345".getBytes()); + System.out.println("\t\t\t... failed"); + fail("Unsupported NID did not throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t\t... passed"); + } + + @Test + public void testAddExtension() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\taddExtension()"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* Test adding supported extensions by NID */ + + /* wolfSSL versions 5.6.3 and earlier did not include code + * fixes to native wolfSSL allowing this extension support to + * work. Use a version > 5.6.3 or apply patch from wolfSSL + * PR 6585 for correct support */ + if (WolfSSL.getLibVersionHex() <= 0x05006003) { + req.addExtension(WolfSSL.NID_key_usage, + "digitalSignature,keyAgreement", false); + req.addExtension(WolfSSL.NID_ext_key_usage, + "serverAuth,clientAuth", false); + } + req.addExtension(WolfSSL.NID_subject_alt_name, + "my test altName", false); + + /* Adding unsupported NID should throw exception */ + try { + req.addExtension(123456, "12345", false); + System.out.println("\t\t\t... failed"); + fail("Unsupported extension NID did not throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test boolean extension setting */ + req.addExtension(WolfSSL.NID_basic_constraints, true, true); + req.addExtension(WolfSSL.NID_basic_constraints, false, true); + + /* Adding unsupported NID should throw exception */ + try { + req.addExtension(123456, true, false); + System.out.println("\t\t\t... failed"); + fail("Unsupported extension NID did not throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t\t... passed"); + } + + @Test + public void testSetVersion() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\tsetVersion()"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + req.setVersion(0); + req.setVersion(1); + + /* Negative versions should throw exception */ + try { + req.setVersion(-100); + System.out.println("\t\t\t... failed"); + fail("Negative version should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t\t... passed"); + } + + @Test + public void testSetPublicKeyFile() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\tsetPublicKey(file)"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* RSA */ + req.setPublicKey(cliKeyPubDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + req.setPublicKey(cliKeyPem, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_PEM); + + /* ECC */ + req.setPublicKey(cliEccKeyDer, WolfSSL.ECDSAk, + WolfSSL.SSL_FILETYPE_ASN1); + req.setPublicKey(cliEccKeyPem, WolfSSL.ECDSAk, + WolfSSL.SSL_FILETYPE_PEM); + + /* Test bad key type */ + try { + req.setPublicKey(cliKeyPubDer, 12345, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("bad key type should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test bad file type */ + try { + req.setPublicKey(cliKeyPubDer, WolfSSL.RSAk, 12345); + System.out.println("\t\t... failed"); + fail("bad file type should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test null file String */ + try { + req.setPublicKey((String)null, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("null PublicKey should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test file that does not exist */ + try { + req.setPublicKey("badfile", WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("Bad path to PublicKey should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t... passed"); + } + + @Test + public void testSetPublicKeyArray() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\tsetPublicKey(array)"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + byte[] cliKeyRSADer = Files.readAllBytes(Paths.get(cliKeyDer)); + byte[] cliKeyRSAPem = Files.readAllBytes(Paths.get(cliKeyPem)); + byte[] cliKeyECCDer = Files.readAllBytes(Paths.get(cliEccKeyDer)); + byte[] cliKeyECCPem = Files.readAllBytes(Paths.get(cliEccKeyPem)); + + /* RSA */ + req.setPublicKey(cliKeyRSADer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + req.setPublicKey(cliKeyRSAPem, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_PEM); + + /* ECC */ + req.setPublicKey(cliKeyECCDer, WolfSSL.ECDSAk, + WolfSSL.SSL_FILETYPE_ASN1); + req.setPublicKey(cliKeyECCPem, WolfSSL.ECDSAk, + WolfSSL.SSL_FILETYPE_PEM); + + /* Test bad key type */ + try { + req.setPublicKey(cliKeyRSADer, 12345, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("bad key type should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test bad file type */ + try { + req.setPublicKey(cliKeyRSADer, WolfSSL.RSAk, 12345); + System.out.println("\t\t... failed"); + fail("bad file type should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test null file String */ + try { + req.setPublicKey((byte[])null, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("null key array should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + /* Test zero-length byte array */ + byte[] zeroArr = new byte[0]; + try { + req.setPublicKey(zeroArr, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + System.out.println("\t\t... failed"); + fail("Zero length pub key array should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t... passed"); + } + + @Test + public void testSetPublicKeyObject() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException, NoSuchAlgorithmException { + + System.out.print("\tsetPublicKey(PublicKey)"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* RSA: 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); + + /* ECC: Set Public Key from generated java.security.PublicKey */ + KeyPairGenerator kpgEcc = KeyPairGenerator.getInstance("EC"); + kpgEcc.initialize(256); + KeyPair keyPairEcc = kpgEcc.generateKeyPair(); + PublicKey pubKeyEcc = keyPairEcc.getPublic(); + req.setPublicKey(pubKeyEcc); + + /* Test null PublicKey object */ + try { + req.setPublicKey((PublicKey)null); + System.out.println("\t\t... failed"); + fail("null PublicKey should throw exception"); + } catch (WolfSSLException e) { + /* expected */ + } + + req.free(); + System.out.println("\t\t... passed"); + } + + @Test + public void testGenCSR_UsingFiles() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\tgen CSR using files"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* Set Subject Name */ + WolfSSLX509Name subjectName = GenerateTestSubjectName(); + assertNotNull(subjectName); + req.setSubjectName(subjectName); + + /* Set Public Key from file */ + req.setPublicKey(cliKeyPubDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + + /* Set Extensions */ + if (WolfSSL.getLibVersionHex() > 0x05006003) { + /* Key Usage and Extended Key Usage only work with wolfSSL + * later than 5.6.3 */ + req.addExtension(WolfSSL.NID_key_usage, + "digitalSignature,keyEncipherment,dataEncipherment", false); + + req.addExtension(WolfSSL.NID_ext_key_usage, + "clientAuth,serverAuth", false); + } + req.addExtension(WolfSSL.NID_subject_alt_name, + "test.wolfssl.com", false); + req.addExtension(WolfSSL.NID_basic_constraints, true, true); + + /* Sign CSR */ + req.signRequest(cliKeyDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1, "SHA256"); + + /* Output to DER and PEM */ + byte[] derCsr = req.getDer(); + byte[] pemCsr = req.getPem(); + + assertNotNull(derCsr); + assertTrue(derCsr.length > 0); + assertNotNull(pemCsr); + assertTrue(pemCsr.length > 0); + + /* Free native memory */ + subjectName.free(); + req.free(); + + System.out.println("\t\t... passed"); + } + + @Test + public void testGenCSR_UsingBuffers() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.print("\tgen CSR using buffers"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* Set Subject Name */ + WolfSSLX509Name subjectName = GenerateTestSubjectName(); + assertNotNull(subjectName); + req.setSubjectName(subjectName); + + /* Set Public Key from file */ + byte[] pubKey = Files.readAllBytes(Paths.get(cliKeyPubDer)); + req.setPublicKey(pubKey, WolfSSL.RSAk, WolfSSL.SSL_FILETYPE_ASN1); + + /* Set Extensions */ + if (WolfSSL.getLibVersionHex() > 0x05006003) { + /* Key Usage and Extended Key Usage only work with wolfSSL + * later than 5.6.3 */ + req.addExtension(WolfSSL.NID_key_usage, + "digitalSignature,keyEncipherment,dataEncipherment", false); + req.addExtension(WolfSSL.NID_ext_key_usage, + "clientAuth,serverAuth", false); + } + req.addExtension(WolfSSL.NID_subject_alt_name, + "test.wolfssl.com", false); + req.addExtension(WolfSSL.NID_basic_constraints, true, true); + + /* Sign CSR */ + byte[] privKey = Files.readAllBytes(Paths.get(cliKeyDer)); + req.signRequest(privKey, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1, "SHA256"); + + /* Output to DER and PEM */ + byte[] derCsr = req.getDer(); + byte[] pemCsr = req.getPem(); + + assertNotNull(derCsr); + assertTrue(derCsr.length > 0); + assertNotNull(pemCsr); + assertTrue(pemCsr.length > 0); + + /* Free native memory */ + subjectName.free(); + req.free(); + + System.out.println("\t\t... passed"); + } + + @Test + public void testGenCSR_UsingJavaClasses() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException, NoSuchAlgorithmException { + + System.out.print("\tgen CSR using Java classes"); + + if (!WolfSSL.certReqEnabled()) { + /* WOLFSSL_CERT_REQ / --enable-certreq not enabled in wolfSSL */ + System.out.println("\t... skipped"); + return; + } + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + /* Set Subject Name */ + WolfSSLX509Name subjectName = GenerateTestSubjectName(); + assertNotNull(subjectName); + 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); + + /* Set Extensions */ + if (WolfSSL.getLibVersionHex() > 0x05006003) { + /* Key Usage and Extended Key Usage only work with wolfSSL + * later than 5.6.3 */ + req.addExtension(WolfSSL.NID_key_usage, + "digitalSignature,keyEncipherment,dataEncipherment", false); + req.addExtension(WolfSSL.NID_ext_key_usage, + "clientAuth,serverAuth", false); + } + req.addExtension(WolfSSL.NID_subject_alt_name, + "test.wolfssl.com", 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 */ + byte[] derCsr = req.getDer(); + byte[] pemCsr = req.getPem(); + + assertNotNull(derCsr); + assertTrue(derCsr.length > 0); + assertNotNull(pemCsr); + assertTrue(pemCsr.length > 0); + + /* Free native memory */ + subjectName.free(); + req.free(); + + System.out.println("\t... passed"); + } + + /* Utility method if needed for testing, print out CSR array to file */ + private void writeOutCsrFile(byte[] csr, String path) + throws IOException { + Files.write(new File(path).toPath(), csr); + } +} + diff --git a/src/test/com/wolfssl/test/WolfSSLTestSuite.java b/src/test/com/wolfssl/test/WolfSSLTestSuite.java index 4bd6762..7adf93d 100644 --- a/src/test/com/wolfssl/test/WolfSSLTestSuite.java +++ b/src/test/com/wolfssl/test/WolfSSLTestSuite.java @@ -31,7 +31,8 @@ import org.junit.runners.Suite; WolfSSLSessionTest.class, WolfCryptRSATest.class, WolfCryptECCTest.class, - WolfSSLCertificateTest.class + WolfSSLCertificateTest.class, + WolfSSLCertRequestTest.class })