Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
22 changes: 22 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <openssl/x509v3.h>
#include <algorithm>
#include <cstring>
#include <string_view>
#if OPENSSL_VERSION_MAJOR >= 3
#include <openssl/provider.h>
#endif
Expand Down Expand Up @@ -1061,6 +1062,27 @@ BIOPointer X509View::getValidTo() const {
return bio;
}

std::optional<std::string_view> X509View::getSignatureAlgorithm() const {
if (cert_ == nullptr) return std::nullopt;
int nid = X509_get_signature_nid(cert_);
if (nid == NID_undef) return std::nullopt;
const char* ln = OBJ_nid2ln(nid);
if (ln == nullptr) return std::nullopt;
return std::string_view(ln);
}

std::optional<std::string> X509View::getSignatureAlgorithmOID() const {
if (cert_ == nullptr) return std::nullopt;
int nid = X509_get_signature_nid(cert_);
if (nid == NID_undef) return std::nullopt;
ASN1_OBJECT* obj = OBJ_nid2obj(nid);
if (obj == nullptr) return std::nullopt;
std::array<char, 128> buf{};
int len = OBJ_obj2txt(buf.data(), buf.size(), obj, 1);
if (len < 0 || static_cast<size_t>(len) >= buf.size()) return std::nullopt;
return std::string(buf.data(), static_cast<size_t>(len));
}

int64_t X509View::getValidToTime() const {
#ifdef OPENSSL_IS_BORINGSSL
// Boringssl does not implement ASN1_TIME_to_tm in a public way,
Expand Down
2 changes: 2 additions & 0 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,8 @@ class X509View final {
BIOPointer getInfoAccess() const;
BIOPointer getValidFrom() const;
BIOPointer getValidTo() const;
std::optional<std::string_view> getSignatureAlgorithm() const;
std::optional<std::string> getSignatureAlgorithmOID() const;
int64_t getValidFromTime() const;
int64_t getValidToTime() const;
DataPointer getSerialNumber() const;
Expand Down
20 changes: 20 additions & 0 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2935,6 +2935,26 @@ added:

The date/time until which this certificate is valid, encapsulated in a `Date` object.

### `x509.signatureAlgorithm`

<!-- YAML
added: REPLACEME
-->

* Type: {string|undefined}

The algorithm used to sign the certificate or `undefined` if the signature algorithm is unknown by OpenSSL.

### `x509.signatureAlgorithmOid`

<!-- YAML
added: REPLACEME
-->

* Type: {string}

The OID of the algorithm used to sign the certificate.

### `x509.verify(publicKey)`

<!-- YAML
Expand Down
20 changes: 20 additions & 0 deletions lib/internal/crypto/x509.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class X509Certificate {
fingerprint512: this.fingerprint512,
keyUsage: this.keyUsage,
serialNumber: this.serialNumber,
signatureAlgorithm: this.signatureAlgorithm,
signatureAlgorithmOid: this.signatureAlgorithmOid,
}, opts)}`;
}

Expand Down Expand Up @@ -285,6 +287,24 @@ class X509Certificate {
return value;
}

get signatureAlgorithm() {
let value = this[kInternalState].get('signatureAlgorithm');
if (value === undefined) {
value = this[kHandle].signatureAlgorithm();
this[kInternalState].set('signatureAlgorithm', value);
}
return value;
}

get signatureAlgorithmOid() {
let value = this[kInternalState].get('signatureAlgorithmOid');
if (value === undefined) {
value = this[kHandle].signatureAlgorithmOid();
this[kInternalState].set('signatureAlgorithmOid', value);
}
return value;
}

get raw() {
let value = this[kInternalState].get('raw');
if (value === undefined) {
Expand Down
50 changes: 50 additions & 0 deletions src/crypto/crypto_x509.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,30 @@ MaybeLocal<Value> GetValidToDate(Environment* env, const X509View& view) {
return Date::New(env->context(), validToTime * 1000.);
}

MaybeLocal<Value> GetSignatureAlgorithm(Environment* env,
const X509View& view) {
auto algo = view.getSignatureAlgorithm();
if (!algo.has_value()) [[unlikely]]
return Undefined(env->isolate());
Local<Value> ret;
if (!ToV8Value(env, algo.value()).ToLocal(&ret)) {
return {};
}
return ret;
}

MaybeLocal<Value> GetSignatureAlgorithmOID(Environment* env,
const X509View& view) {
auto oid = view.getSignatureAlgorithmOID();
if (!oid.has_value()) [[unlikely]]
return Undefined(env->isolate());
Local<Value> ret;
if (!ToV8Value(env, oid.value()).ToLocal(&ret)) {
return {};
}
return ret;
}

MaybeLocal<Value> GetSerialNumber(Environment* env, const X509View& view) {
if (auto serial = view.getSerialNumber()) {
return OneByteString(env->isolate(),
Expand Down Expand Up @@ -342,6 +366,26 @@ void ValidToDate(const FunctionCallbackInfo<Value>& args) {
}
}

void SignatureAlgorithm(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
X509Certificate* cert;
ASSIGN_OR_RETURN_UNWRAP(&cert, args.This());
Local<Value> ret;
if (GetSignatureAlgorithm(env, cert->view()).ToLocal(&ret)) {
args.GetReturnValue().Set(ret);
}
}

void SignatureAlgorithmOID(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
X509Certificate* cert;
ASSIGN_OR_RETURN_UNWRAP(&cert, args.This());
Local<Value> ret;
if (GetSignatureAlgorithmOID(env, cert->view()).ToLocal(&ret)) {
args.GetReturnValue().Set(ret);
}
}

void SerialNumber(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
X509Certificate* cert;
Expand Down Expand Up @@ -822,6 +866,10 @@ Local<FunctionTemplate> X509Certificate::GetConstructorTemplate(
SetProtoMethodNoSideEffect(isolate, tmpl, "validFrom", ValidFrom);
SetProtoMethodNoSideEffect(isolate, tmpl, "validToDate", ValidToDate);
SetProtoMethodNoSideEffect(isolate, tmpl, "validFromDate", ValidFromDate);
SetProtoMethodNoSideEffect(isolate, tmpl, "signatureAlgorithm",
SignatureAlgorithm);
SetProtoMethodNoSideEffect(isolate, tmpl, "signatureAlgorithmOid",
SignatureAlgorithmOID);
SetProtoMethodNoSideEffect(
isolate, tmpl, "fingerprint", Fingerprint<Digest::SHA1>);
SetProtoMethodNoSideEffect(
Expand Down Expand Up @@ -996,6 +1044,8 @@ void X509Certificate::RegisterExternalReferences(
registry->Register(ValidFrom);
registry->Register(ValidToDate);
registry->Register(ValidFromDate);
registry->Register(SignatureAlgorithm);
registry->Register(SignatureAlgorithmOID);
registry->Register(Fingerprint<Digest::SHA1>);
registry->Register(Fingerprint<Digest::SHA256>);
registry->Register(Fingerprint<Digest::SHA512>);
Expand Down
3 changes: 3 additions & 0 deletions test/parallel/test-crypto-x509.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ const der = Buffer.from(
assert.strictEqual(x509.keyUsage, undefined);
assert.strictEqual(x509.serialNumber.toUpperCase(), '147D36C1C2F74206DE9FAB5F2226D78ADB00A426');

assert.strictEqual(x509.signatureAlgorithm, 'sha256WithRSAEncryption');
assert.strictEqual(x509.signatureAlgorithmOid, '1.2.840.113549.1.1.11');

assert.deepStrictEqual(x509.raw, der);

if (!process.features.openssl_is_boringssl) {
Expand Down
Loading