Add a CSR example using a raw ECC public key (Qx/Qy or X9.63)
parent
0227d4073d
commit
11e039f999
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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…
Reference in New Issue