diff --git a/doc/dox_comments/header_files/asn_public.h b/doc/dox_comments/header_files/asn_public.h index fe6903ba9..eec57f9ee 100644 --- a/doc/dox_comments/header_files/asn_public.h +++ b/doc/dox_comments/header_files/asn_public.h @@ -1249,6 +1249,29 @@ WOLFSSL_API int wc_KeyPemToDer(const unsigned char*, int, WOLFSSL_API int wc_CertPemToDer(const unsigned char*, int, unsigned char*, int, int); +/*! + \ingroup CertsKeys + + \brief This function gets the public key in DER format from a populated + DecodedCert struct. Users must call wc_InitDecodedCert() and wc_ParseCert() + before calling this API. wc_InitDecodedCert() accepts a DER/ASN.1 encoded + certificate. To convert a PEM cert to DER, first use wc_CertPemToDer() + before calling wc_InitDecodedCert(). + + \return 0 on success, negative on error. LENGTH_ONLY_E if derKey is NULL + and returning length only. + + \param cert populated DecodedCert struct holding X.509 certificate + \param derKey output buffer to place DER encoded public key + \param derKeySz [IN/OUT] size of derKey buffer on input, size of public key + on return. If derKey is passed in as NULL, derKeySz will be set to required + buffer size for public key and LENGTH_ONLY_E will be returned from function. + + \sa wc_GetPubKeyDerFromCert +*/ +WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, + byte* derKey, word32* derKeySz); + /*! \ingroup ASN diff --git a/tests/api.c b/tests/api.c index df7227682..bd1ecf71a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -30094,6 +30094,8 @@ static void test_wc_CertPemToDer(void) free(cert_der); if (cert_buf) free(cert_buf); + + printf(resultFmt, passed); #endif } @@ -30129,6 +30131,8 @@ static void test_wc_PubKeyPemToDer(void) free(cert_der); if (cert_buf) free(cert_buf); + + printf(resultFmt, passed); #endif #endif } @@ -30152,9 +30156,172 @@ static void test_wc_PemPubKeyToDer(void) free(cert_der); } + + printf(resultFmt, passed); #endif } +static void test_wc_GetPubKeyDerFromCert(void) +{ +#if !defined(NO_RSA) || defined(HAVE_ECC) + int ret; + word32 idx = 0; + byte keyDer[TWOK_BUF]; /* large enough for up to RSA 2048 */ + word32 keyDerSz = (word32)sizeof(keyDer); + DecodedCert decoded; +#if !defined(NO_RSA) && defined(WOLFSSL_CERT_REQ) + byte certBuf[6000]; /* for PEM and CSR, client-cert.pem is 5-6kB */ + word32 certBufSz = sizeof(certBuf); +#endif +#if (!defined(USE_CERT_BUFFERS_2048) && !defined(USE_CERT_BUFFERS_1024)) || \ + defined(WOLFSSL_CERT_REQ) + XFILE fp; +#endif +#ifndef NO_RSA + RsaKey rsaKey; + #if defined(USE_CERT_BUFFERS_2048) + byte* rsaCertDer = (byte*)client_cert_der_2048; + word32 rsaCertDerSz = sizeof_client_cert_der_2048; + #elif defined(USE_CERT_BUFFERS_1024) + byte* rsaCertDer = (byte*)client_cert_der_1024; + word32 rsaCertDerSz = sizeof_client_cert_der_1024; + #else + unsigned char rsaCertDer[TWOK_BUF]; + word32 rsaCertDerSz; + #endif +#endif +#ifdef HAVE_ECC + ecc_key eccKey; + #if defined(USE_CERT_BUFFERS_256) + byte* eccCert = (byte*)cliecc_cert_der_256; + word32 eccCertSz = sizeof_cliecc_cert_der_256; + #else + unsigned char eccCert[ONEK_BUF]; + word32 eccCertSz; + XFILE fp2; + #endif +#endif + + printf(testingFmt, "wc_GetPubKeyDerFromCert()"); + +#ifndef NO_RSA + +#if !defined(USE_CERT_BUFFERS_1024) && !defined(USE_CERT_BUFFERS_2048) + fp = XFOPEN("./certs/1024/client-cert.der", "rb"); + AssertTrue((fp != XBADFILE)); + rsaCertDerSz = (word32)XFREAD(rsaCertDer, 1, sizeof(rsaCertDer), fp); + XFCLOSE(fp); +#endif + + /* good test case - RSA DER cert */ + wc_InitDecodedCert(&decoded, rsaCertDer, rsaCertDerSz, NULL); + ret = wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL); + AssertIntEQ(ret, 0); + + ret = wc_GetPubKeyDerFromCert(&decoded, keyDer, &keyDerSz); + AssertIntEQ(ret, 0); + AssertIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_InitRsaKey(&rsaKey, NULL); + AssertIntEQ(ret, 0); + ret = wc_RsaPublicKeyDecode(keyDer, &idx, &rsaKey, keyDerSz); + AssertIntEQ(ret, 0); + wc_FreeRsaKey(&rsaKey); + + /* test LENGTH_ONLY_E case */ + keyDerSz = 0; + ret = wc_GetPubKeyDerFromCert(&decoded, NULL, &keyDerSz); + AssertIntEQ(ret, LENGTH_ONLY_E); + AssertIntGT(keyDerSz, 0); + + /* bad args: DecodedCert NULL */ + ret = wc_GetPubKeyDerFromCert(NULL, keyDer, &keyDerSz); + AssertIntEQ(ret, BAD_FUNC_ARG); + + /* bad args: output key buff size */ + ret = wc_GetPubKeyDerFromCert(&decoded, keyDer, NULL); + AssertIntEQ(ret, BAD_FUNC_ARG); + + /* bad args: zero size output key buffer */ + keyDerSz = 0; + ret = wc_GetPubKeyDerFromCert(&decoded, keyDer, &keyDerSz); + AssertIntEQ(ret, BAD_FUNC_ARG); + + wc_FreeDecodedCert(&decoded); + + /* Certificate Request Tests */ + #ifdef WOLFSSL_CERT_REQ + { + XMEMSET(certBuf, 0, sizeof(certBuf)); + fp = XFOPEN("./certs/csr.signed.der", "rb"); + AssertTrue((fp != XBADFILE)); + certBufSz = (word32)XFREAD(certBuf, 1, certBufSz, fp); + XFCLOSE(fp); + + wc_InitDecodedCert(&decoded, certBuf, certBufSz, NULL); + ret = wc_ParseCert(&decoded, CERTREQ_TYPE, NO_VERIFY, NULL); + AssertIntEQ(ret, 0); + + /* good test case - RSA DER certificate request */ + keyDerSz = sizeof(keyDer); + ret = wc_GetPubKeyDerFromCert(&decoded, keyDer, &keyDerSz); + AssertIntEQ(ret, 0); + AssertIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_InitRsaKey(&rsaKey, NULL); + AssertIntEQ(ret, 0); + idx = 0; + ret = wc_RsaPublicKeyDecode(keyDer, &idx, &rsaKey, keyDerSz); + AssertIntEQ(ret, 0); + wc_FreeRsaKey(&rsaKey); + + wc_FreeDecodedCert(&decoded); + } + #endif /* WOLFSSL_CERT_REQ */ +#endif /* NO_RSA */ + +#ifdef HAVE_ECC + #ifndef USE_CERT_BUFFERS_256 + fp2 = XFOPEN("./certs/client-ecc-cert.der", "rb"); + AssertTrue((fp2 != XBADFILE)); + eccCertSz = (word32)XFREAD(eccCert, 1, ONEK_BUF, fp2); + XFCLOSE(fp2); + #endif + + wc_InitDecodedCert(&decoded, eccCert, eccCertSz, NULL); + ret = wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL); + AssertIntEQ(ret, 0); + + /* good test case - ECC */ + XMEMSET(keyDer, 0, sizeof(keyDer)); + keyDerSz = sizeof(keyDer); + ret = wc_GetPubKeyDerFromCert(&decoded, keyDer, &keyDerSz); + AssertIntEQ(ret, 0); + AssertIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_ecc_init(&eccKey); + AssertIntEQ(ret, 0); + idx = 0; /* reset idx to 0, used above in RSA case */ + ret = wc_EccPublicKeyDecode(keyDer, &idx, &eccKey, keyDerSz); + AssertIntEQ(ret, 0); + wc_ecc_free(&eccKey); + + /* test LENGTH_ONLY_E case */ + keyDerSz = 0; + ret = wc_GetPubKeyDerFromCert(&decoded, NULL, &keyDerSz); + AssertIntEQ(ret, LENGTH_ONLY_E); + AssertIntGT(keyDerSz, 0); + + wc_FreeDecodedCert(&decoded); +#endif + + printf(resultFmt, passed); + +#endif /* !NO_RSA || HAVE_ECC */ +} static void test_wolfSSL_certs(void) { @@ -51933,6 +52100,7 @@ void ApiTest(void) test_wc_CertPemToDer(); test_wc_PubKeyPemToDer(); test_wc_PemPubKeyToDer(); + test_wc_GetPubKeyDerFromCert(); /*OCSP Stapling. */ AssertIntEQ(test_wolfSSL_UseOCSPStapling(), WOLFSSL_SUCCESS); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 75fde24a9..e663b5466 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -17245,12 +17245,16 @@ static int DecodeCertReq(DecodedCert* cert, int* criticalExt) int ParseCert(DecodedCert* cert, int type, int verify, void* cm) { int ret; +#ifndef WOLFSSL_NO_MALLOC char* ptr; +#endif ret = ParseCertRelative(cert, type, verify, cm); if (ret < 0) return ret; +#ifndef WOLFSSL_NO_MALLOC + /* cert->subjectCN not stored as copy of WOLFSSL_NO_MALLOC defind */ if (cert->subjectCNLen > 0) { ptr = (char*) XMALLOC(cert->subjectCNLen + 1, cert->heap, DYNAMIC_TYPE_SUBJECT_CN); @@ -17261,7 +17265,10 @@ int ParseCert(DecodedCert* cert, int type, int verify, void* cm) cert->subjectCN = ptr; cert->subjectCNStored = 1; } +#endif +#ifndef WOLFSSL_NO_MALLOC + /* cert->publicKey not stored as copy if WOLFSSL_NO_MALLOC defined */ if (cert->keyOID == RSAk && cert->publicKey != NULL && cert->pubKeySize > 0) { ptr = (char*) XMALLOC(cert->pubKeySize, cert->heap, @@ -17272,6 +17279,7 @@ int ParseCert(DecodedCert* cert, int type, int verify, void* cm) cert->publicKey = (byte *)ptr; cert->pubKeyStored = 1; } +#endif return ret; } @@ -19950,6 +19958,57 @@ int wc_PemPubKeyToDer(const char* fileName, #endif /* !NO_FILESYSTEM && WOLFSSL_PEM_TO_DER */ +/* Get public key in DER format from a populated DecodedCert struct. + * + * Users must call wc_InitDecodedCert() and wc_ParseCert() before calling + * this API. wc_InitDecodedCert() accepts a DER/ASN.1 encoded certificate. + * To convert a PEM cert to DER first use wc_CertPemToDer() before calling + * wc_InitDecodedCert(). + * + * cert - populated DecodedCert struct holding X.509 certificate + * derKey - output buffer to place DER/ASN.1 encoded public key + * derKeySz [IN/OUT] - size of derKey buffer on input, size of public key + * on return. If derKey is passed in as NULL, derKeySz + * will be set to required buffer size for public key + * and LENGTH_ONLY_E will be returned from function. + * Returns 0 on success, or negative error code on failure. LENGTH_ONLY_E + * if derKey is NULL and returning length only. + */ +int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, + byte* derKey, word32* derKeySz) +{ + int ret = 0; + + /* derKey may be NULL to return length only */ + if (cert == NULL || derKeySz == NULL || + (derKey != NULL && *derKeySz == 0)) { + return BAD_FUNC_ARG; + } + + if (cert->publicKey == NULL) { + WOLFSSL_MSG("DecodedCert does not contain public key\n"); + return BAD_FUNC_ARG; + } + + /* if derKey is NULL, return required output buffer size in derKeySz */ + if (derKey == NULL) { + *derKeySz = cert->pubKeySize; + ret = LENGTH_ONLY_E; + } + + if (ret == 0) { + if (cert->pubKeySize > *derKeySz) { + WOLFSSL_MSG("Output buffer not large enough for public key DER"); + ret = BAD_FUNC_ARG; + } + else { + XMEMCPY(derKey, cert->publicKey, cert->pubKeySize); + *derKeySz = cert->pubKeySize; + } + } + + return ret; +} #if !defined(NO_RSA) && (defined(WOLFSSL_CERT_GEN) || \ defined(WOLFSSL_KCAPI_RSA) || \ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e6bbc54d7..84ac501cd 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -12238,10 +12238,10 @@ WOLFSSL_TEST_SUBROUTINE int memory_test(void) #ifdef WOLFSSL_CERT_GEN static const char* otherCertPemFile = CERT_WRITE_TEMP_DIR "othercert.pem"; static const char* certPemFile = CERT_WRITE_TEMP_DIR "cert.pem"; - #endif - #ifdef WOLFSSL_CERT_REQ - static const char* certReqDerFile = CERT_WRITE_TEMP_DIR "certreq.der"; - static const char* certReqPemFile = CERT_WRITE_TEMP_DIR "certreq.pem"; + #ifdef WOLFSSL_CERT_REQ + static const char* certReqDerFile = CERT_WRITE_TEMP_DIR "certreq.der"; + static const char* certReqPemFile = CERT_WRITE_TEMP_DIR "certreq.pem"; + #endif #endif #endif /* !NO_RSA */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 350e34859..d9b7d95d1 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2327,9 +2327,9 @@ enum { /* ssl Constants */ WOLFSSL_UNKNOWN = -2, WOLFSSL_FATAL_ERROR = -1, - WOLFSSL_FILETYPE_ASN1 = 2, - WOLFSSL_FILETYPE_PEM = 1, - WOLFSSL_FILETYPE_DEFAULT = 2, /* ASN1 */ + WOLFSSL_FILETYPE_ASN1 = CTC_FILETYPE_ASN1, + WOLFSSL_FILETYPE_PEM = CTC_FILETYPE_PEM, + WOLFSSL_FILETYPE_DEFAULT = CTC_FILETYPE_ASN1, /* ASN1 */ WOLFSSL_VERIFY_NONE = 0, WOLFSSL_VERIFY_PEER = 1 << 0, diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 00c58dcd2..61de72216 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -200,6 +200,9 @@ enum Ctc_Misc { CTC_MAX_ALT_SIZE = WC_CTC_MAX_ALT_SIZE, /* may be huge, default: 16384 */ CTC_SERIAL_SIZE = 20, CTC_GEN_SERIAL_SZ = 16, + CTC_FILETYPE_ASN1 = 2, + CTC_FILETYPE_PEM = 1, + CTC_FILETYPE_DEFAULT = 2, #ifdef WOLFSSL_CERT_EXT /* AKID could contains: hash + (Option) AuthCertIssuer,AuthCertSerialNum * We support only hash */ @@ -766,6 +769,9 @@ WOLFSSL_API void wc_InitDecodedCert(struct DecodedCert*, const byte*, word32, vo WOLFSSL_API void wc_FreeDecodedCert(struct DecodedCert*); WOLFSSL_API int wc_ParseCert(struct DecodedCert*, int, int, void*); +WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, + byte* derKey, word32* derKeySz); + #ifdef __cplusplus } /* extern "C" */ #endif