Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions certgen/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ CFLAGS+=$(OPTIMIZE)
#LIBS+=$(STATIC_LIB)
LIBS+=$(DYN_LIB)

all:certgen_example certgen_ca_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb custom_ext custom_ext_callback
all:certgen_example certgen_ca_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb csr_ecc_rawpub custom_ext custom_ext_callback

certgen_example:certgen_example.o
$(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS)
Expand All @@ -49,6 +49,9 @@ csr_sign:csr_sign.o
csr_cryptocb:csr_cryptocb.o
$(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS)

csr_ecc_rawpub:csr_ecc_rawpub.o
$(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS)

custom_ext:custom_ext.o
$(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS)

Expand All @@ -58,16 +61,17 @@ custom_ext_callback:custom_ext_callback.o
.PHONY: clean all check

clean:
rm -f *.o certgen_example certgen_ca_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb custom_ext custom_ext_callback
rm -f *.o certgen_example certgen_ca_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb csr_ecc_rawpub custom_ext custom_ext_callback
rm -f newCert.*

check: certgen_example certgen_ca_example csr_w_ed25519_example csr_example csr_sign csr_cryptocb custom_ext custom_ext_callback
check: certgen_example certgen_ca_example csr_w_ed25519_example csr_example csr_sign csr_cryptocb csr_ecc_rawpub custom_ext custom_ext_callback
out=$$(./certgen_example) && printf '%s' "$$out" | grep -q 'Tests passed'
out=$$(./certgen_ca_example) && printf '%s' "$$out" | grep -q 'Tests passed'
out=$$(./csr_w_ed25519_example 2>&1) && printf '%s' "$$out" | grep -q -- '-----END CERTIFICATE REQUEST-----'
out=$$(./csr_example ecc) && printf '%s' "$$out" | grep -q 'Saved PEM to "ecc-csr.pem"'
out=$$(./csr_sign ecc-csr.pem ca-ecc-cert.der ca-ecc-key.der) && printf '%s' "$$out" | grep -q 'Tests passed'
out=$$(./csr_cryptocb ecc) && printf '%s' "$$out" | grep -q 'Saved CSR PEM to "ecc-csr.pem"'
out=$$(./csr_ecc_rawpub) && printf '%s' "$$out" | grep -q 'Tests passed'
out=$$(./custom_ext) && printf '%s' "$$out" | grep -q 'Tests passed'
out=$$(./custom_ext_callback newCert.der) && printf '%s' "$$out" | grep -q 'Success'
@echo "PASS: certgen checks"
37 changes: 37 additions & 0 deletions certgen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,43 @@ GxI=
```


## Certificate Signing Request (CSR) Example with a Raw ECC Public Key

Example of building a CSR around an ECC public key supplied as raw bytes - no DER SubjectPublicKeyInfo and no PEM. A secure element or HSM usually hands back either the affine coordinates Qx and Qy or the X9.63 point `0x04||X||Y`; `csr_ecc_rawpub` imports both forms with `wc_ecc_import_unsigned()` and `wc_ecc_import_x963_ex()`.

Passing `d = NULL` to `wc_ecc_import_unsigned()` leaves the key public only, which is all `wc_MakeCertReq_ex()` needs. Where the private key lives in hardware, give that public key a `devId` so `wc_SignCert_ex()` routes to a crypto callback - see the `csr_cryptocb` example below for the callback plumbing.

Tested with these wolfSSL build options:

```sh
./autogen.sh # If cloned from GitHub
./configure --enable-certreq --enable-certgen --enable-certext --enable-ecc
make
sudo make install
sudo ldconfig # required on some targets
```

### `csr_ecc_rawpub` Example output

```
% ./csr_ecc_rawpub
Request body from Qx/Qy and from the X9.63 point match: 212 bytes
Signed CSR: 302 bytes
Parsed back and signature verified
-----BEGIN CERTIFICATE REQUEST-----
MIIBKjCB0QIBADBvMQswCQYDVQQGEwJVUzELMAkGA1UECAwCT1IxETAPBgNVBAcM
CFBvcnRsYW5kMRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVu
dDEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAE/rF//42PvQmzLaTkHm7U2bD2GPRw4szv7AKfFthiMdT0MY612CDl4BIM
6rQ44CAfH1CA2urhIBlGCdMlqMcFFKAAMAoGCCqGSM49BAMCA0gAMEUCIFBPIc/G
Hyv95pXPNQN9E0d+ojt4Gcpq17P8sPJ+BTP6AiEAwc8VcNY28gWDq/74FBa5Lv9d
XvvZV3GtIFgiBDzaOI4=
-----END CERTIFICATE REQUEST-----

Tests passed
```


## Certificate Signing Request (CSR) Example with Crypto Callbacks

Example of generating a PEM-encoded certificate signing request (CSR) using the
Expand Down
280 changes: 280 additions & 0 deletions certgen/csr_ecc_rawpub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/* csr_ecc_rawpub.c
*
* Copyright (C) 2006-2026 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
*/

/* Certificate signing request built around an ECC public key supplied as raw
* bytes - no DER SubjectPublicKeyInfo and no PEM. A secure element or HSM
* typically hands back either the affine coordinates Qx and Qy or the X9.63
* point 0x04||X||Y; both forms are shown here.
*
* The key material below is a throw-away P-256 key, standing in for whatever
* the device holds. Where the private key lives in hardware, drop the private
* import and give the public key a devId instead, so wc_SignCert_ex() calls a
* crypto callback - see csr_cryptocb.c.
*/

#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/error-crypt.h>

#include <stdio.h>
#include <string.h>

#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \
defined(HAVE_ECC) && !defined(NO_SHA256) && !defined(WC_NO_RNG)

#define CSR_BUF_SZ 1024

/* P-256 public key, affine coordinates, big endian, 32 bytes each. */
static const byte eccQx[] = {
0xFE, 0xB1, 0x7F, 0xFF, 0x8D, 0x8F, 0xBD, 0x09,
0xB3, 0x2D, 0xA4, 0xE4, 0x1E, 0x6E, 0xD4, 0xD9,
0xB0, 0xF6, 0x18, 0xF4, 0x70, 0xE2, 0xCC, 0xEF,
0xEC, 0x02, 0x9F, 0x16, 0xD8, 0x62, 0x31, 0xD4
};
static const byte eccQy[] = {
0xF4, 0x31, 0x8E, 0xB5, 0xD8, 0x20, 0xE5, 0xE0,
0x12, 0x0C, 0xEA, 0xB4, 0x38, 0xE0, 0x20, 0x1F,
0x1F, 0x50, 0x80, 0xDA, 0xEA, 0xE1, 0x20, 0x19,
0x46, 0x09, 0xD3, 0x25, 0xA8, 0xC7, 0x05, 0x14
};
/* Matching private scalar, used here only to sign the request. */
static const byte eccD[] = {
0xF2, 0x52, 0xDE, 0x2D, 0x5E, 0x75, 0xDD, 0x94,
0xA7, 0xE1, 0x56, 0xDE, 0x8D, 0x12, 0x91, 0x0A,
0x26, 0x54, 0x0F, 0xCD, 0x58, 0x30, 0xB6, 0xAF,
0xF8, 0x41, 0xDA, 0x8C, 0x16, 0xCE, 0x46, 0xD1
};

/* Same public key as an X9.63 uncompressed point: 0x04 || X || Y. */
static byte eccPoint[1 + sizeof(eccQx) + sizeof(eccQy)];

static void make_point(void)
{
eccPoint[0] = 0x04;
XMEMCPY(eccPoint + 1, eccQx, sizeof(eccQx));
XMEMCPY(eccPoint + 1 + sizeof(eccQx), eccQy, sizeof(eccQy));
}

static void fill_subject(Cert* req)
{
strncpy(req->subject.country, "US", CTC_NAME_SIZE);
strncpy(req->subject.state, "OR", CTC_NAME_SIZE);
strncpy(req->subject.locality, "Portland", CTC_NAME_SIZE);
strncpy(req->subject.org, "wolfSSL", CTC_NAME_SIZE);
strncpy(req->subject.unit, "Development", CTC_NAME_SIZE);
strncpy(req->subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);
req->version = 0;
req->sigType = CTC_SHA256wECDSA;
}

/* Encode the unsigned request body around pubKey. Returns its size. */
static int make_req_body(ecc_key* pubKey, Cert* req, byte* der, word32 derBufSz)
{
int ret;

ret = wc_InitCert(req);
if (ret != 0) {
printf("wc_InitCert failed: %d\n", ret);
return ret;
}
fill_subject(req);

ret = wc_MakeCertReq_ex(req, der, derBufSz, ECC_TYPE, pubKey);
if (ret <= 0) {
printf("wc_MakeCertReq_ex failed: %d\n", ret);
}
return ret;
}

#ifdef WOLFSSL_DER_TO_PEM
static int print_pem(byte* der, int derSz)
{
byte pem[CSR_BUF_SZ * 2];
int pemSz;

pemSz = wc_DerToPem(der, (word32)derSz, pem, (word32)sizeof(pem),
CERTREQ_TYPE);
if (pemSz <= 0) {
printf("wc_DerToPem failed: %d\n", pemSz);
return pemSz;
}
printf("%.*s\n", pemSz, pem);
return 0;
}
#endif

int main(void)
{
ecc_key pubKey;
ecc_key pointKey;
ecc_key signKey;
WC_RNG rng;
Cert req;
static DecodedCert decoded;
byte der[CSR_BUF_SZ];
byte derPoint[CSR_BUF_SZ];
int ret;
int derSz;
int pointSz;

make_point();

ret = wolfCrypt_Init();
if (ret != 0) {
printf("wolfCrypt_Init failed: %d\n", ret);
return ret;
}

ret = wc_InitRng(&rng);
if (ret != 0) {
printf("wc_InitRng failed: %d\n", ret);
wolfCrypt_Cleanup();
return ret;
}

/* Zero every key first so the cleanup below is safe even if one of the
* inits fails partway. */
XMEMSET(&signKey, 0, sizeof(signKey));
XMEMSET(&pubKey, 0, sizeof(pubKey));
XMEMSET(&pointKey, 0, sizeof(pointKey));

ret = wc_ecc_init(&signKey);
if (ret == 0)
ret = wc_ecc_init(&pubKey);
if (ret == 0)
ret = wc_ecc_init(&pointKey);
if (ret != 0) {
printf("wc_ecc_init failed: %d\n", ret);
goto exit;
}

/* The signer. A device would leave this out and route wc_SignCert_ex()
* to its secure element through a crypto callback. */
ret = wc_ecc_import_unsigned(&signKey, eccQx, eccQy, eccD,
ECC_SECP256R1);
if (ret != 0) {
printf("private wc_ecc_import_unsigned failed: %d\n", ret);
goto exit;
}

/* Public key from bare Qx and Qy. A NULL private scalar leaves the key
* public only, which is all the request body needs. */
ret = wc_ecc_import_unsigned(&pubKey, eccQx, eccQy, NULL,
ECC_SECP256R1);
if (ret != 0) {
printf("wc_ecc_import_unsigned failed: %d\n", ret);
goto exit;
}

/* The same key from the X9.63 point. */
ret = wc_ecc_import_x963_ex(eccPoint, (word32)sizeof(eccPoint), &pointKey,
ECC_SECP256R1);
if (ret != 0) {
printf("wc_ecc_import_x963_ex failed: %d\n", ret);
goto exit;
}

derSz = make_req_body(&pubKey, &req, der, (word32)sizeof(der));
if (derSz <= 0) {
ret = derSz;
goto exit;
}
pointSz = make_req_body(&pointKey, &req, derPoint,
(word32)sizeof(derPoint));
if (pointSz <= 0) {
ret = pointSz;
goto exit;
}

/* The two import forms describe the same key, so the request bodies
* must be byte for byte identical. */
if ((derSz != pointSz) || (XMEMCMP(der, derPoint, (size_t)derSz) != 0)) {
printf("Request bodies differ: %d vs %d bytes\n", derSz, pointSz);
ret = -1;
goto exit;
}
printf("Request body from Qx/Qy and from the X9.63 point match: "
"%d bytes\n", derSz);

/* Rebuild the body for the key being signed, so req describes der. */
derSz = make_req_body(&pubKey, &req, der, (word32)sizeof(der));
if (derSz <= 0) {
ret = derSz;
goto exit;
}

ret = wc_SignCert_ex(req.bodySz, req.sigType, der, (word32)sizeof(der),
ECC_TYPE, &signKey, &rng);
if (ret <= 0) {
printf("wc_SignCert_ex failed: %d\n", ret);
goto exit;
}
derSz = ret;
printf("Signed CSR: %d bytes\n", derSz);

/* Parse the request back and check its signature, so a malformed CSR
* fails rather than reporting success. */
wc_InitDecodedCert(&decoded, der, (word32)derSz, NULL);
ret = wc_ParseCert(&decoded, CERTREQ_TYPE, VERIFY, NULL);
if (ret == 0) {
printf("Parsed back and signature verified\n");
}
else {
printf("wc_ParseCert failed: %d\n", ret);
}
wc_FreeDecodedCert(&decoded);
if (ret != 0) {
goto exit;
}

#ifdef WOLFSSL_DER_TO_PEM
ret = print_pem(der, derSz);
if (ret != 0) {
goto exit;
}
#endif

printf("Tests passed\n");
ret = 0;

exit:
wc_ecc_free(&pointKey);
wc_ecc_free(&pubKey);
wc_ecc_free(&signKey);
wc_FreeRng(&rng);
wolfCrypt_Cleanup();
return ret;
}

#else

int main(void)
{
printf("Please compile wolfSSL with --enable-certgen --enable-certreq "
"--enable-certext --enable-ecc, and with SHA-256 and the RNG "
"enabled\n");
return 0;
}

#endif
Loading