Skip to content

Commit 5498ee6

Browse files
panvanodejs-github-bot
authored andcommitted
crypto: add X.509 generation with CSR support
Expose callback-based generateX509Certificate() and synchronous generateX509CertificateSync(), producing X509Certificate objects for self-signed and issuer-signed X.509 v3 certificates. Accept grouped distinguished names or exact subject DER, public keys, serial numbers, validity dates, and an explicit signing digest. Reuse crypto.sign() key inputs and RSA options, including OpenSSL STORE-backed keys. A PKCS#10 request can supply the subject and public key instead. Require a synchronous selectExtensions callback to accept, replace, or amend the requested extensions. Verify the request signature before signing the certificate, in the selected sync or async mode. Provide typed Basic Constraints, Key Usage, Extended Key Usage, SAN, SKI, and AKI, plus arbitrary raw extensions. Add none implicitly and leave raw extension semantics to the caller. Copy mutable inputs before queueing asynchronous work. Certificate trust and CA authorization remain the caller's responsibility. Fixes: #51169 Assisted-by: Codex Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #67245 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 77e6379 commit 5498ee6

18 files changed

Lines changed: 3636 additions & 23 deletions

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,16 +1742,13 @@ std::optional<X509NameInspection> InspectName(const X509_NAME* name) {
17421742
return output;
17431743
}
17441744

1745+
template <typename GetExtension>
17451746
std::optional<std::vector<X509RawExtension>> InspectExtensions(
1746-
const X509* certificate) {
1747-
if (certificate == nullptr) return std::nullopt;
1748-
const int count = X509_get_ext_count(certificate);
1749-
if (count < 0) return std::nullopt;
1750-
1747+
OPENSSL_SIZE_T count, GetExtension get_extension) {
17511748
std::vector<X509RawExtension> output;
17521749
output.reserve(static_cast<size_t>(count));
1753-
for (int i = 0; i < count; i++) {
1754-
const X509_EXTENSION* extension = X509_get_ext(certificate, i);
1750+
for (OPENSSL_SIZE_T i = 0; i < count; i++) {
1751+
const X509_EXTENSION* extension = get_extension(i);
17551752
if (extension == nullptr) return std::nullopt;
17561753

17571754
auto* mutable_extension = const_cast<X509_EXTENSION*>(extension);
@@ -1770,6 +1767,15 @@ std::optional<std::vector<X509RawExtension>> InspectExtensions(
17701767
}
17711768
} // namespace
17721769

1770+
std::optional<std::vector<X509RawExtension>> inspectX509Extensions(
1771+
const STACK_OF(X509_EXTENSION) * extensions) {
1772+
if (extensions == nullptr) return std::nullopt;
1773+
return InspectExtensions(sk_X509_EXTENSION_num(extensions),
1774+
[extensions](OPENSSL_SIZE_T index) {
1775+
return sk_X509_EXTENSION_value(extensions, index);
1776+
});
1777+
}
1778+
17731779
std::optional<int64_t> X509View::getVersion() const {
17741780
MarkPopErrorOnReturn mark_pop_error_on_return;
17751781
if (cert_ == nullptr) return std::nullopt;
@@ -1793,7 +1799,13 @@ std::optional<X509NameInspection> X509View::inspectIssuerName() const {
17931799
std::optional<std::vector<X509RawExtension>> X509View::inspectExtensions()
17941800
const {
17951801
MarkPopErrorOnReturn mark_pop_error_on_return;
1796-
return InspectExtensions(cert_);
1802+
if (cert_ == nullptr) return std::nullopt;
1803+
const int count = X509_get_ext_count(cert_);
1804+
if (count < 0) return std::nullopt;
1805+
return InspectExtensions(
1806+
static_cast<OPENSSL_SIZE_T>(count), [this](OPENSSL_SIZE_T index) {
1807+
return X509_get_ext(cert_, static_cast<int>(index));
1808+
});
17971809
}
17981810

17991811
bool X509View::isIssuedBy(const X509View& issuer) const {
@@ -1803,10 +1815,39 @@ bool X509View::isIssuedBy(const X509View& issuer) const {
18031815
const_cast<X509*>(cert_)) == X509_V_OK;
18041816
}
18051817

1806-
bool X509View::checkPrivateKey(const EVPKeyPointer& pkey) const {
1807-
ClearErrorOnReturn clearErrorOnReturn;
1808-
if (cert_ == nullptr || pkey == nullptr) return false;
1809-
return X509_check_private_key(const_cast<X509*>(cert_), pkey.get()) == 1;
1818+
X509View::CheckKeyResult X509View::checkPrivateKey(
1819+
const EVPKeyPointer& pkey) const {
1820+
MarkPopErrorOnReturn mark_pop_error_on_return;
1821+
if (cert_ == nullptr || pkey == nullptr) {
1822+
return CheckKeyResult::OPERATION_FAILED;
1823+
}
1824+
1825+
EVPKeyPointer certificate_key(X509_get_pubkey(const_cast<X509*>(cert_)));
1826+
if (!certificate_key) return CheckKeyResult::OPERATION_FAILED;
1827+
#if NCRYPTO_USE_BORINGSSL
1828+
if (EVP_PKEY_is_opaque(certificate_key.get()) ||
1829+
EVP_PKEY_is_opaque(pkey.get())) {
1830+
return CheckKeyResult::UNSUPPORTED_OPERATION;
1831+
}
1832+
#endif
1833+
#if NCRYPTO_USE_OPENSSL_PROVIDER
1834+
const int result = EVP_PKEY_eq(certificate_key.get(), pkey.get());
1835+
#elif NCRYPTO_USE_BORINGSSL
1836+
const int result = EVP_PKEY_cmp(certificate_key.get(), pkey.get());
1837+
#else
1838+
#error "Unsupported crypto backend"
1839+
#endif
1840+
switch (result) {
1841+
case 1:
1842+
return CheckKeyResult::MATCH;
1843+
case 0:
1844+
case -1:
1845+
return CheckKeyResult::NO_MATCH;
1846+
case -2:
1847+
return CheckKeyResult::UNSUPPORTED_OPERATION;
1848+
default:
1849+
return CheckKeyResult::OPERATION_FAILED;
1850+
}
18101851
}
18111852

18121853
bool X509View::checkPublicKey(const EVPKeyPointer& pkey) const {

‎deps/ncrypto/ncrypto.h‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@
7070
#define NCRYPTO_HAS_ASN1_BIT_STRING_GET_LENGTH 0
7171
#endif
7272

73+
// BoringSSL's X.509 signer can encode only its restricted RSA-PSS parameter
74+
// profile. Other supported backends accept the full runtime signing profile.
75+
#if NCRYPTO_USE_BORINGSSL
76+
#define NCRYPTO_HAS_X509_RSA_PSS_FLEXIBLE_PARAMETERS 0
77+
#elif NCRYPTO_USE_OPENSSL_PROVIDER
78+
#define NCRYPTO_HAS_X509_RSA_PSS_FLEXIBLE_PARAMETERS 1
79+
#else
80+
#error "Unsupported crypto backend"
81+
#endif
82+
7383
#if NCRYPTO_USE_OPENSSL_PROVIDER
7484
#include <openssl/core_names.h>
7585
#include <openssl/encoder.h>
@@ -1683,8 +1693,18 @@ struct X509RawExtension final {
16831693
std::vector<unsigned char> value;
16841694
};
16851695

1696+
std::optional<std::vector<X509RawExtension>> inspectX509Extensions(
1697+
const STACK_OF(X509_EXTENSION) * extensions);
1698+
16861699
class X509View final {
16871700
public:
1701+
enum class CheckKeyResult {
1702+
MATCH, // The requested comparison passed.
1703+
NO_MATCH, // It completed and produced an ordinary mismatch.
1704+
UNSUPPORTED_OPERATION, // The backend cannot compare these keys.
1705+
OPERATION_FAILED, // The backend failed for another reason.
1706+
};
1707+
16881708
static X509View From(const SSLPointer& ssl);
16891709
static X509View From(const SSLCtxPointer& ctx);
16901710

@@ -1727,7 +1747,7 @@ class X509View final {
17271747

17281748
bool isCA() const;
17291749
bool isIssuedBy(const X509View& other) const;
1730-
bool checkPrivateKey(const EVPKeyPointer& pkey) const;
1750+
CheckKeyResult checkPrivateKey(const EVPKeyPointer& pkey) const;
17311751
bool checkPublicKey(const EVPKeyPointer& pkey) const;
17321752

17331753
std::optional<std::string> getFingerprint(const Digest& method) const;

0 commit comments

Comments
 (0)