Switch from node-forge to PKI.js (#13894)

* Switch parse-pki-cert from node-forge to PKI.js

This replaces the implementation of parse-pki-cert to use PKI.js rather
than node-forge for two reasons:

 - PKI.js uses Web Crypto rather than maintaining a built-in
   implementation of several algorithms.
 - node-forge presently lacks support for ECDSA and Ed25519
   certificates.

Related: #13680

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Add dependency on PKI.js

$ yarn add -D asn1js pvutils pkijs

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Remove dependency on node-forge

$ yarn remove node-forge

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

* Add changelog entry

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
Alexander Scheel 2022-02-04 12:52:28 -05:00 committed by GitHub
parent 2b3661b863
commit dcdfaa8889
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 13 deletions

3
changelog/13894.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Add support for ECDSA and Ed25519 certificate views
```

View File

@ -1,5 +1,7 @@
import { helper } from '@ember/component/helper';
import { pki } from 'node-forge';
import * as asn1js from 'asn1js';
import { fromBase64, stringToArrayBuffer } from 'pvutils';
import { Certificate } from 'pkijs';
export function parsePkiCert([model]) {
// model has to be the responseJSON from PKI serializer
@ -7,18 +9,45 @@ export function parsePkiCert([model]) {
return;
}
let cert;
// node-forge cannot parse EC (elliptical curve) certs
// set canParse to false if unable to convert a Forge cert from PEM
try {
cert = pki.certificateFromPem(model.certificate);
let cert_base64 = model.certificate.replace(/(-----(BEGIN|END) CERTIFICATE-----|\n)/g, '');
let cert_der = fromBase64(cert_base64);
let cert_asn1 = asn1js.fromBER(stringToArrayBuffer(cert_der));
cert = new Certificate({ schema: cert_asn1.result });
} catch (error) {
console.log('Error parsing certificate:', error, model.certificate);
return {
can_parse: false,
};
}
const commonName = cert?.subject.getField('CN') ? cert.subject.getField('CN').value : null;
const expiryDate = cert?.validity.notAfter;
const issueDate = cert?.validity.notBefore;
// We wish to get the CN element out of this certificate's subject. A
// subject is a list of RDNs, where each RDN is a (type, value) tuple
// and where a type is an OID. The OID for CN can be found here:
//
// http://oid-info.com/get/2.5.4.3
// https://datatracker.ietf.org/doc/html/rfc5280#page-112
//
// Each value is then encoded as another ASN.1 object; in the case of a
// CommonName field, this is usually a PrintableString, BMPString, or a
// UTF8String. Regardless of encoding, it should be present in the
// valueBlock's value field if it is renderable.
const commonNameOID = '2.5.4.3';
const commonNames = cert?.subject?.typesAndValues
.filter((rdn) => rdn?.type === commonNameOID)
.map((rdn) => rdn?.value?.valueBlock?.value);
// Theoretically, there might be multiple (or no) CommonNames -- but Vault
// presently refuses to issue certificates without CommonNames in most
// cases. For now, return the first CommonName we find. Alternatively, we
// might update our callers to handle multiple, or join them using some
// separator like ','.
const commonName = commonNames ? (commonNames.length ? commonNames[0] : null) : null;
// Date instances are stored in the value field as the notAfter/notBefore
// field themselves are Time values.
const expiryDate = cert?.notAfter?.value;
const issueDate = cert?.notBefore?.value;
return {
can_parse: true,
common_name: commonName,

View File

@ -67,6 +67,7 @@
"@icholy/duration": "^5.1.0",
"@storybook/cli": "^6.3.10",
"@storybook/ember-cli-storybook": "^0.4.0",
"asn1js": "^2.2.0",
"autosize": "^4.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-inline-json-import": "^0.3.2",
@ -156,13 +157,14 @@
"jsondiffpatch": "^0.4.1",
"jsonlint": "^1.6.3",
"loader.js": "^4.7.0",
"node-forge": "^0.10.0",
"node-sass": "^4.10.0",
"normalize.css": "4.1.1",
"npm-run-all": "^4.1.5",
"pkijs": "^2.2.2",
"pretender": "^3.4.3",
"prettier": "^2.2.1",
"prettier-eslint-cli": "^5.0.0",
"pvutils": "^1.0.17",
"qunit": "^2.14.1",
"qunit-dom": "^1.6.0",
"route-recognizer": "^0.3.4",

View File

@ -5087,6 +5087,13 @@ asn1@~0.2.3:
dependencies:
safer-buffer "~2.1.0"
asn1js@^2.1.1, asn1js@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-2.2.0.tgz#d890fcdda86b8a005693df14a986bfb2c2069c57"
integrity sha512-oagLNqpfNv7CvmyMoexMDNyVDSiq1rya0AEUgcLlNHdHgNl6U/hi8xY370n5y+ZIFEXOx0J4B1qF2NDjMRxklA==
dependencies:
pvutils latest
assert-never@^1.1.0, assert-never@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe"
@ -7353,6 +7360,11 @@ bytes@3.1.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
bytestreamjs@^1.0.29:
version "1.0.29"
resolved "https://registry.yarnpkg.com/bytestreamjs/-/bytestreamjs-1.0.29.tgz#691f8ee8e5a150c61b925993a3eec0911ed17f1d"
integrity sha512-Mri3yqoo9YvdaSvD5OYl4Rdu9zCBJInW/Ez31sdlNY4ikMy//EvTTmidfLcs0e+NBvKVEpPzYvJAesjgMdjnZg==
cacache@^12.0.2:
version "12.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
@ -16125,11 +16137,6 @@ node-fetch@^2.6.1:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-gyp@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
@ -17142,6 +17149,15 @@ pkg-up@^2.0.0:
dependencies:
find-up "^2.1.0"
pkijs@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-2.2.2.tgz#d0780379a2fb80c892c3ead4bcfb42675b67a982"
integrity sha512-xRTEW9LgUeHBe5hRCC4EHvLbO6o3L/t8uEk8cTaSMVEjEcy2G5qJ/bY2ndvrGgZlKDThnTrM4Xlwu8Qpyr6mOg==
dependencies:
asn1js "^2.1.1"
bytestreamjs "^1.0.29"
pvutils "^1.0.17"
please-upgrade-node@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
@ -17615,6 +17631,11 @@ puppeteer-core@^2.1.1:
rimraf "^2.6.1"
ws "^6.1.0"
pvutils@^1.0.17, pvutils@latest:
version "1.0.17"
resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.0.17.tgz#ade3c74dfe7178944fe44806626bd2e249d996bf"
integrity sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==
q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"