diff --git a/certgen/Makefile b/certgen/Makefile index 870b6f27..49f08126 100644 --- a/certgen/Makefile +++ b/certgen/Makefile @@ -13,7 +13,7 @@ CFLAGS=-I$(WOLF_INSTALL_DIR)/include -Wall LIBS=-L$(WOLF_INSTALL_DIR)/lib -lwolfssl -all:certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb +all:certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb custom_ext custom_ext_callback certgen_example:certgen_example.o $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) @@ -30,8 +30,14 @@ csr_sign:csr_sign.o csr_cryptocb:csr_cryptocb.o $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) +custom_ext:custom_ext.o + $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) + +custom_ext_callback:custom_ext_callback.o + $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) + .PHONY: clean all clean: - rm -f *.o certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb + rm -f *.o certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb custom_ext custom_ext_callback rm -f newCert.* diff --git a/certgen/README.md b/certgen/README.md index 3c1884e9..899354f8 100644 --- a/certgen/README.md +++ b/certgen/README.md @@ -336,3 +336,89 @@ b0B3b2xmc3NsLmNvbTAqMAUGAytlcAMhAOZXWxMbx1EUa+079dH6q55stusCCaOZ (448) Saved CSR PEM to "ed25519-csr.pem" ``` + +## Certificate Generation and Parsing with Custom Extensions Example + +Example of generating a PEM-encoded and DER-encoded certificate with custom +extensions. We then parse those certificates and display the custom extensions +using a callback that is called for each unknown extension that is encountered. + +Tested with these wolfSSL build options: + +```sh +./autogen.sh # If cloned from GitHub +./configure --enable-asn=template --enable-certreq --enable-keygen --enable-certgen --enable-certext CFLAGS="-DWOLFSSL_TEST_CERT -DHAVE_OID_DECODING -DHAVE_OID_ENCODING -DWOLFSSL_CUSTOM_OID -DWOLFSSL_CERT_EXT" +make +make check +sudo make install +sudo ldconfig # required on some targets +``` + +In the directory where this README.md file is found, build and execute the +`custom_ext` and `custom_ext_callback` samples: + +```sh +make custom_ext +make custom_ext_callback +./custom_ext +./custom_ext_callback newCert.der +``` + +For independent verfication of the presence of the extensions, you can +pretty-print the certificates using `openssl`: + +``` +openssl x509 -in newCert.pem -noout -text +``` + +The output should be similar to this: +``` +ert.pem -noout -text +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 3c:26:f1:35:59:78:2c:1b:56:5f:3d:9c:be:eb:5a:1d + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C = US, ST = Washington, L = Seattle, O = wolfSSL, OU = Development, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Validity + Not Before: Mar 3 21:42:52 2022 GMT + Not After : Jul 17 21:42:52 2023 GMT + Subject: C = US, ST = MT, L = Bozeman, O = yourOrgNameHere, OU = yourUnitNameHere, CN = www.yourDomain.com, emailAddress = yourEmail@yourDomain.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:e2:3c:29:23:9f:3d:e4:07:09:2d:61:df:7b:5f: + ef:32:cd:17:84:b6:84:b7:44:90:39:39:77:6b:a3: + 72:45:88:bd:3f:3a:8a:a7:1d:e6:f0:09:2c:ba:1a: + 6b:cf:62:a8:a6:d5:5b:83:21:dc:0e:73:5f:0e:06: + f2:53:06:7c:c8:ea:67:82:df:79:4e:18:1b:e2:16: + cc:97:aa:d6:72:75:2f:1f:ca:65:e1:40:5b:95:e6: + d5:14:ea:de:f1:c1:39:c6:11:3d:a6:01:7b:63:57: + 55:8e:b7:d4:54:2e:e2:83:18:a6:74:11:d1:38:87: + d0:83:09:80:22:0d:41:ac:cf:40:d4:a1:23:b9:97: + 52:b1:e0:88:4d:48:b4:5e:c5:ef:63:c6:3c:e8:42: + d7:0d:b0:4a:fe:e1:c4:76:06:4a:a0:a9:0e:0c:45: + af:7f:ec:de:78:b8:53:7e:d1:a2:ea:9d:d6:12:3c: + a9:cb:88:2d:55:b6:fa:57:d0:28:3e:f1:c0:14:ce: + 92:3a:6c:23:56:21:3f:e7:72:d5:8f:94:ee:be:fa: + 86:d0:80:6b:3d:bd:ab:b3:5e:08:fb:50:c0:73:0c: + 90:18:d3:c3:db:f9:62:56:7f:51:b2:c2:63:b1:00: + 1c:6e:da:a3:06:07:52:57:d0:64:cd:a2:11:9f:2d: + 93:6b + Exponent: 65537 (0x10001) + X509v3 extensions: + 1.2.3.4.5: critical + This is a critical extension + 1.2.3.4.6: + This is NOT a critical extension + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:7b:5f:77:56:5d:c7:c7:06:8e:bf:c4:95:fa:cc: + 71:c8:e8:77:61:6c:7d:d3:84:1d:53:c3:2e:02:5c:77:06:e8: + 02:21:00:bf:f0:b4:0f:5c:fd:2b:16:92:35:43:7e:dc:59:bd: + 0f:8e:c0:82:e9:54:5d:57:7f:0e:e6:7d:5a:46:27:75:cb + +``` + +Note the section titled "X509v3 extensions:". diff --git a/certgen/custom_ext.c b/certgen/custom_ext.c new file mode 100644 index 00000000..26f314a8 --- /dev/null +++ b/certgen/custom_ext.c @@ -0,0 +1,259 @@ +/* custom_ext.c + * + * Copyright (C) 2006-2021 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 +#include +#include +#include +#include +#include +#include + +#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CERT_GEN) && \ + defined(WOLFSSL_KEY_GEN) && defined(WOLFSSL_CUSTOM_OID) && \ + defined(HAVE_OID_ENCODING) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_RSA) + +#define HEAP_HINT NULL +#define LARGE_TEMP_SZ 4096 + +static int do_certgen(int argc, char** argv) +{ + int ret = 0; + + Cert newCert; + + FILE* file; + char certToUse[] = "./ca-ecc-cert.der"; + char caKeyFile[] = "./ca-ecc-key.der"; + char newCertOutput[] = "./newCert.der"; + + int derBufSz; + int caKeySz; + + byte* derBuf = NULL; + byte* pemBuf = NULL; + byte* caKeyBuf = NULL; + + /* for MakeCert and SignCert */ + WC_RNG rng; + ecc_key caKey; + RsaKey newKey; + word32 idx = 0; + int initRng = 0, initCaKey = 0, initNewKey = 0; + +#ifdef WOLFSSL_DER_TO_PEM + char pemOutput[] = "./newCert.pem"; + int pemBufSz; +#endif + + /*------------------------------------------------------------------------*/ + /* open the CA der formatted certificate, we need to get it's subject line + * to use in the new cert we're creating as the "Issuer" line */ + /*------------------------------------------------------------------------*/ + printf("Loading CA certificate\n"); + + derBuf = (byte*)XMALLOC(LARGE_TEMP_SZ, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (derBuf == NULL) goto exit; + XMEMSET(derBuf, 0, LARGE_TEMP_SZ); + + file = fopen(certToUse, "rb"); + if (!file) { + printf("failed to open file: %s\n", certToUse); + goto exit; + } + derBufSz = fread(derBuf, 1, LARGE_TEMP_SZ, file); + fclose(file); + + printf("Successfully read %d bytes from %s\n\n", derBufSz, certToUse); + + /*------------------------------------------------------------------------*/ + /* open caKey file and get the caKey, we need it to sign our new cert */ + /*------------------------------------------------------------------------*/ + printf("Loading the CA key\n"); + + caKeyBuf = (byte*)XMALLOC(LARGE_TEMP_SZ, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (caKeyBuf == NULL) goto exit; + XMEMSET(caKeyBuf, 0, LARGE_TEMP_SZ); + + file = fopen(caKeyFile, "rb"); + if (!file) { + printf("failed to open file: %s\n", caKeyFile); + goto exit; + } + caKeySz = fread(caKeyBuf, 1, LARGE_TEMP_SZ, file); + fclose(file); + if (caKeySz <= 0) { + printf("Failed to read caKey from file\n"); + goto exit; + } + + printf("Successfully read %d bytes from %s\n", caKeySz, caKeyFile); + + wc_ecc_init(&caKey); + initCaKey = 1; + + printf("Decoding the CA private key\n"); + idx = 0; + ret = wc_EccPrivateKeyDecode(caKeyBuf, &idx, &caKey, (word32)caKeySz); + if (ret != 0) goto exit; + + printf("Successfully loaded CA Key\n\n"); + + /*------------------------------------------------------------------------*/ + /* Generate new private key to go with our new cert */ + /*------------------------------------------------------------------------*/ + ret = wc_InitRng(&rng); + if (ret != 0) goto exit; + initRng = 1; + + printf("Generating a new RSA key\n"); + ret = wc_InitRsaKey(&newKey, NULL); + if (ret != 0) goto exit; + initNewKey = 1; + + wc_MakeRsaKey(&newKey, 2048, WC_RSA_EXPONENT, &rng); + if (ret != 0) goto exit; + + printf("Successfully created new RSA key\n\n"); + + /*------------------------------------------------------------------------*/ + /* Create a new certificate using SUBJECT information from ca cert + * for ISSUER information in generated cert */ + /*------------------------------------------------------------------------*/ + printf("Setting new cert issuer to subject of signer\n"); + + wc_InitCert(&newCert); + + strncpy(newCert.subject.country, "US", CTC_NAME_SIZE); + strncpy(newCert.subject.state, "MT", CTC_NAME_SIZE); + strncpy(newCert.subject.locality, "Bozeman", CTC_NAME_SIZE); + strncpy(newCert.subject.org, "yourOrgNameHere", CTC_NAME_SIZE); + strncpy(newCert.subject.unit, "yourUnitNameHere", CTC_NAME_SIZE); + strncpy(newCert.subject.commonName, "www.yourDomain.com", CTC_NAME_SIZE); + strncpy(newCert.subject.email, "yourEmail@yourDomain.com", CTC_NAME_SIZE); + + newCert.isCA = 0; + newCert.sigType = CTC_SHA256wECDSA; + + ret = wc_SetIssuerBuffer(&newCert, derBuf, derBufSz); + if (ret != 0) goto exit; + + ret = wc_SetCustomExtension(&newCert, 1, "1.2.3.4.5", (const byte *)"This is a critical extension", 28); + if (ret < 0) goto exit; + + ret = wc_SetCustomExtension(&newCert, 0, "1.2.3.4.6", (const byte *)"This is NOT a critical extension", 32); + if (ret < 0) goto exit; + + ret = wc_MakeCert(&newCert, derBuf, LARGE_TEMP_SZ, &newKey, NULL, &rng); + if (ret < 0) goto exit; + printf("Make Cert returned %d\n", ret); + + ret = wc_SignCert(newCert.bodySz, newCert.sigType, derBuf, LARGE_TEMP_SZ, + NULL, &caKey, &rng); + if (ret < 0) goto exit; + printf("Signed Cert returned %d\n", ret); + + derBufSz = ret; + + printf("Successfully created new certificate\n\n"); + + /*------------------------------------------------------------------------*/ + /* write the new cert to file in der format */ + /*------------------------------------------------------------------------*/ + printf("Writing newly generated DER certificate to file \"%s\"\n", + newCertOutput); + file = fopen(newCertOutput, "wb"); + if (!file) { + printf("failed to open file: %s\n", newCertOutput); + goto exit; + } + + ret = (int)fwrite(derBuf, 1, derBufSz, file); + fclose(file); + printf("Successfully output %d bytes\n", ret); + +#ifdef WOLFSSL_DER_TO_PEM + /*------------------------------------------------------------------------*/ + /* convert the der to a pem and write it to a file */ + /*------------------------------------------------------------------------*/ + printf("Convert the DER cert to PEM formatted cert\n"); + + pemBuf = (byte*)XMALLOC(LARGE_TEMP_SZ, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (pemBuf == NULL) goto exit; + XMEMSET(pemBuf, 0, LARGE_TEMP_SZ); + + pemBufSz = wc_DerToPem(derBuf, derBufSz, pemBuf, LARGE_TEMP_SZ, CERT_TYPE); + if (pemBufSz < 0) goto exit; + + printf("Resulting PEM buffer is %d bytes\n", pemBufSz); + + file = fopen(pemOutput, "wb"); + if (!file) { + printf("failed to open file: %s\n", pemOutput); + goto exit; + } + fwrite(pemBuf, 1, pemBufSz, file); + fclose(file); + printf("Successfully converted the DER to PEM to \"%s\"\n\n", + pemOutput); +#endif + + ret = 0; /* success */ + +exit: + + XFREE(derBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(pemBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(caKeyBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + if (initCaKey) + wc_ecc_free(&caKey); + if (initNewKey) + wc_FreeRsaKey(&newKey); + if (initRng) { + wc_FreeRng(&rng); + } + + if (ret == 0) + printf("Tests passed\n"); + else + printf("Failure code was %d\n", ret); + return ret; +} + +int main(int argc, char** argv) +{ + return do_certgen(argc, argv); +} + +#else + +int main(int argc, char** argv) +{ + printf("Please compile wolfSSL with --enable-asn=template --enable-certgen " + "--enable-keygen CFLAGS=\"-DWOLFSSL_CUSTOM_OID -DHAVE_OID_ENCODING " + "-DWOLFSSL_CERT_EXT\""); + return 0; +} + +#endif diff --git a/certgen/custom_ext_callback.c b/certgen/custom_ext_callback.c new file mode 100644 index 00000000..945b691a --- /dev/null +++ b/certgen/custom_ext_callback.c @@ -0,0 +1,151 @@ +/* + * custom_ext_callback.c + * + * Copyright (C) 2006-2022 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 + */ + +/* This is based off of ecc-key-decode.c */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CERT_REQ) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_TEST_CERT) && \ + defined(HAVE_OID_DECODING) && defined(WOLFSSL_CUSTOM_OID) && \ + defined(WOLFSSL_CERT_EXT) + +static int myCustomExtCallback(const word16* oid, word32 oidSz, int crit, + const unsigned char* der, word32 derSz) { + word32 i; + + printf("Custom Extension found!\n"); + printf("("); + for (i = 0; i < oidSz; i++) { + printf("%d", oid[i]); + if (i < oidSz - 1) { + printf("."); + } + } + printf(") : "); + + if (crit) { + printf("CRITICAL"); + } else { + printf("NOT CRITICAL"); + } + printf(" : "); + + for (i = 0; i < derSz; i ++) { + printf("%x ", der[i]); + } + printf("\n"); + + /* NOTE: by returning zero, we are accepting this extension and informing + * wolfSSL that it is acceptable. If you find an extension that you + * do not find acceptable, you should return an error. The standard + * behavior upon encountering an unknown extension with the critical + * flag set is to return ASN_CRIT_EXT_E. For the sake of brevity, + * this example is always accepting every extension; you should use + * different logic. */ + return 0; +} + +static void check_ret(char*, int); + +#ifndef MAX_BUF +#define MAX_BUF 4096 +#endif + +int main(int argc, char** argv) +{ + DecodedCert decodedCert; + FILE* file; + byte derBuffer[MAX_BUF]; + size_t bytes; + int ret; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + if (argc != 2) { + printf("Usage: %s certifcate.der\n", argv[0]); + return 0; + } + + wolfCrypt_Init(); + + /* TODO: change this once we start generating our own. */ + file = fopen(argv[1], "rb"); + if (!file) { + printf("Failed to open file\n"); + exit(-99); + } + + bytes = fread(derBuffer, 1, sizeof(derBuffer), file); + fclose(file); + + printf("read bytes = %d\n", (int) bytes); + if (bytes <= 0) { + return -1; + } + + InitDecodedCert(&decodedCert, derBuffer, (word32) bytes, 0); + + ret = wc_SetUnknownExtCallback(&decodedCert, myCustomExtCallback); + check_ret("wc_SetUnknownExtCallback", ret); + + ret = ParseCert(&decodedCert, CERT_TYPE, NO_VERIFY, NULL); + check_ret("ParseCert", ret); + + FreeDecodedCert(&decodedCert); + + wolfCrypt_Cleanup(); + + printf("Success\n"); + + return 0; +} + +static void check_ret(char* call, int ret) +{ + if (ret != 0) { + printf("call: %s\n", call); + printf("ret = %d\n", ret); + exit(-99); + } + return; +} + +#else + +int main(void) +{ + printf("Not compiled in: Build wolfSSL using ./configure --enable-asn=template --enable-certreq --enable-certgen CFLAGS=\"-DWOLFSSL_TEST_CERT -DHAVE_OID_DECODING -DWOLFSSL_CUSTOM_OID -DWOLFSSL_CERT_EXT\"\n"); + return 0; +} + +#endif + diff --git a/ecc/ecc-key-decode.c b/ecc/ecc-key-decode.c index 59663c07..e4b5e126 100644 --- a/ecc/ecc-key-decode.c +++ b/ecc/ecc-key-decode.c @@ -34,11 +34,15 @@ static void check_ret(char*, int); +#ifndef MAX_BUF +#define MAX_BUF 4096 +#endif + int main(void) { DecodedCert decodedCert; FILE* file; - byte derBuffer[4096]; + byte derBuffer[MAX_BUF]; size_t bytes; ecc_key eccKey; int ret; @@ -56,7 +60,7 @@ int main(void) exit(-99); } - bytes = fread(derBuffer, 1, 4096, file); + bytes = fread(derBuffer, 1, sizeof(derBuffer), file); fclose(file); printf("read bytes = %d\n", (int) bytes);