Created
April 22, 2020 03:26
-
-
Save tcd93/f62fe2b2c1bcd532a2f0bb54b8810982 to your computer and use it in GitHub Desktop.
Simple way to generate certificate signing request (CSR) with X.509 using Javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as asn1js from 'asn1js'; | |
import { getCrypto, getAlgorithmParameters, CertificationRequest, AttributeTypeAndValue } from 'pkijs/build' | |
import { arrayBufferToString, toBase64 } from 'pvutils'; | |
//https://github.com/PeculiarVentures/PKI.js/blob/31c10e9bb879cac59d710102adf4fd7bd61cd66c/src/CryptoEngine.js#L1300 | |
const hashAlg = 'SHA-256' | |
const signAlg = 'ECDSA' | |
/** | |
* @example | |
* createPKCS10({ enrollmentID: 'user1', organizationUnit: 'Marketing', organization: 'Farmer Market', state: 'M', country: 'V' }) | |
* .then(({csr, privateKey} => {...})) | |
*/ | |
export async function createPKCS10({ enrollmentID, organizationUnit, organization, state, country }) | |
{ | |
const crypto = getWebCrypto() | |
const keyPair = await generateKeyPair(crypto, getAlgorithm(signAlg, hashAlg)) | |
return { | |
csr: `-----BEGIN CERTIFICATE REQUEST-----\n${ | |
formatPEM( | |
toBase64( | |
arrayBufferToString( | |
await createCSR(keyPair, hashAlg, { enrollmentID, organizationUnit, organization, state, country }) | |
) | |
) | |
)}\n-----END CERTIFICATE REQUEST-----`, | |
privateKey: `-----BEGIN PRIVATE KEY-----\n${ | |
toBase64(arrayBufferToString(await crypto.exportKey('pkcs8', keyPair.privateKey))) | |
}\n-----END PRIVATE KEY-----` | |
} | |
} | |
async function createCSR(keyPair, hashAlg, { enrollmentID, organizationUnit, organization, state, country }) | |
{ | |
const pkcs10 = new CertificationRequest() | |
pkcs10.version = 0 | |
//list of OID reference: http://oidref.com/2.5.4 | |
pkcs10.subject.typesAndValues.push(new AttributeTypeAndValue({ | |
type: '2.5.4.6', //countryName | |
value: new asn1js.PrintableString({ value: country }) | |
})) | |
pkcs10.subject.typesAndValues.push(new AttributeTypeAndValue({ | |
type: '2.5.4.8', //stateOrProvinceName | |
value: new asn1js.Utf8String({ value: state }) | |
})) | |
pkcs10.subject.typesAndValues.push(new AttributeTypeAndValue({ | |
type: '2.5.4.10', //organizationName | |
value: new asn1js.Utf8String({ value: organization }) | |
})) | |
pkcs10.subject.typesAndValues.push(new AttributeTypeAndValue({ | |
type: '2.5.4.11', //organizationUnitName | |
value: new asn1js.Utf8String({ value: organizationUnit }) | |
})) | |
pkcs10.subject.typesAndValues.push(new AttributeTypeAndValue({ | |
type: '2.5.4.3', //commonName | |
value: new asn1js.Utf8String({ value: enrollmentID }) | |
})) | |
//add attributes to make CSR valid | |
//Attributes must be "a0:00" if empty | |
pkcs10.attributes = [] | |
await pkcs10.subjectPublicKeyInfo.importKey(keyPair.publicKey) | |
//signing final PKCS#10 request | |
await pkcs10.sign(keyPair.privateKey, hashAlg) | |
return pkcs10.toSchema().toBER(false) | |
} | |
// add line break every 64th character | |
function formatPEM(pemString) | |
{ | |
return pemString.replace(/(.{64})/g, '$1\n') | |
} | |
function getWebCrypto() { | |
const crypto = getCrypto() | |
if(typeof crypto === 'undefined') | |
throw 'No WebCrypto extension found' | |
return crypto | |
} | |
function getAlgorithm(signAlg, hashAlg) { | |
const algorithm = getAlgorithmParameters(signAlg, 'generatekey') | |
if('hash' in algorithm.algorithm) | |
algorithm.algorithm.hash.name = hashAlg | |
return algorithm | |
} | |
function generateKeyPair(crypto, algorithm) { | |
return crypto.generateKey(algorithm.algorithm, true, algorithm.usages) | |
} | |
/** | |
* to learn more about asn1, ber & der, attributes & types used in pkcs#10 | |
* http://luca.ntop.org/Teaching/Appunti/asn1.html | |
* | |
* guides to SubtleCrypto (which PKIjs is built upon): | |
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment