Last active
June 9, 2021 15:29
-
-
Save fmonniot/e94ed40e3902415ccb9ca5a1d932297e to your computer and use it in GitHub Desktop.
Generate a root CA (self-signed), a CSR and a leaf certificate
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 { writeFileSync, mkdirSync } from "fs"; | |
import * as x509 from "@peculiar/x509"; | |
import { Crypto } from "@peculiar/webcrypto"; | |
import { execSync, spawnSync } from "child_process"; | |
// We're okay with setting a global config for a simple script | |
const crypto = new Crypto(); | |
x509.cryptoProvider.set(crypto); | |
const algorithm = { | |
name: "ECDSA", | |
namedCurve: "P-256", | |
hash: "SHA-256", | |
}; | |
main() | |
.then(() => console.log("done")) | |
.catch((e) => console.error(`failed: ${e}`)); | |
async function main() { | |
const path = `${__dirname}/certs`; | |
try { | |
mkdirSync(path); | |
} catch (error) { | |
if (error.code === "EEXIST") { | |
/* folder already exists, continue */ | |
} else { | |
throw error; | |
} | |
} | |
const nodeId = "85cde111bdfc4034"; | |
const fabricId = "733452ca227e0f54"; | |
// CA | |
const [ca_pem, ca_private] = await gen_ca(); | |
writeFileSync(path + "root.crt", ca_pem); | |
writeFileSync(path + "root.key", ca_private); | |
// CSR | |
const [csr_pem, csr_private] = await gen_csr(); | |
writeFileSync(path + "child.csr", csr_pem); | |
writeFileSync(path + "child.key", csr_private); | |
// CRT | |
writeFileSync( | |
path + "child.crt", | |
await issue_cert(ca_pem, ca_private, csr_pem, nodeId, fabricId) | |
); | |
// OpenSSL checks | |
const is_csr_valid = openssl_verify_req(path + "child.csr"); | |
const is_crt_valid = openssl_verify_cert(path, "child.crt"); | |
console.log(`csr_valid=${is_csr_valid}; crt_valid=${is_crt_valid}`); | |
} | |
function openssl_verify_req(path: string): boolean { | |
let res = true; | |
try { | |
const process = spawnSync("openssl", [ | |
"req", | |
"-noout", | |
"-verify", | |
"-in", | |
path, | |
]); | |
const err = process.stderr.toString(); | |
res = /verify OK/.test(err); | |
} catch (error) { | |
console.log(error); | |
res = false; | |
} | |
return res; | |
} | |
function openssl_verify_cert(folder: string, file_name: string): boolean { | |
let res = true; | |
try { | |
execSync( | |
`openssl verify -verbose -CAfile ${folder}root.crt ${folder}${file_name}` | |
); | |
} catch (error) { | |
res = false; | |
} | |
return res; | |
} | |
// Voluntary working with string, as that's what we will have access to in the application | |
async function issue_cert( | |
ca_pem: string, | |
ca_private: string, | |
csr_pem: string, | |
node_id: string, | |
fabric_id: string | undefined | |
) { | |
const root = new x509.X509Certificate(ca_pem); | |
const csr = new x509.Pkcs10CertificateRequest(csr_pem); | |
const prv = await crypto.subtle.importKey( | |
"pkcs8", | |
x509.PemConverter.decode(ca_private)[0], | |
algorithm, | |
true, | |
["sign", "verify"] | |
); | |
const cert = await x509.X509CertificateGenerator.create({ | |
serialNumber: "02", | |
notBefore: new Date("2020/01/01"), | |
notAfter: new Date("2022/01/02"), | |
signingAlgorithm: algorithm, | |
extensions: [ | |
new x509.BasicConstraintsExtension(false, 2, true), | |
new x509.KeyUsagesExtension( | |
x509.KeyUsageFlags.digitalSignature | | |
x509.KeyUsageFlags.keyEncipherment, | |
true | |
), | |
], | |
issuer: root.subject, | |
subject: `1.3.6.1.4.1.37244.1.1=${node_id}, 1.3.6.1.4.1.37244.1.5=${fabric_id}`, | |
publicKey: await csr.publicKey.export(crypto), | |
signingKey: prv, | |
}); | |
return cert.toString("pem"); | |
} | |
// return [ca, private_key] | |
async function gen_ca(): Promise<[string, string]> { | |
const keys = await crypto.subtle.generateKey(algorithm, true, [ | |
"sign", | |
"verify", | |
]); | |
const cert = await x509.X509CertificateGenerator.createSelfSigned({ | |
serialNumber: "01", | |
notBefore: new Date("2020/01/01"), | |
notAfter: new Date("2022/01/02"), | |
signingAlgorithm: algorithm, | |
extensions: [ | |
new x509.BasicConstraintsExtension(true, 2, true), | |
new x509.KeyUsagesExtension( | |
x509.KeyUsageFlags.keyCertSign | | |
x509.KeyUsageFlags.cRLSign | | |
x509.KeyUsageFlags.digitalSignature | | |
x509.KeyUsageFlags.keyEncipherment, | |
true | |
), | |
], | |
keys, | |
name: `1.3.6.1.4.1.37244.1.4=a36adda4dc1a34fb`, | |
}); | |
const prv = await crypto.subtle.exportKey("pkcs8", keys.privateKey); | |
const pem = x509.PemConverter.encode(prv, "PRIVATE KEY"); | |
return [cert.toString("pem"), pem]; | |
} | |
// return [csr, private_key] | |
async function gen_csr() { | |
const keys = await crypto.subtle.generateKey(algorithm, true, [ | |
"sign", | |
"verify", | |
]); | |
const csr = await x509.Pkcs10CertificateRequestGenerator.create({ | |
name: "CN=ignored", | |
keys, | |
signingAlgorithm: algorithm, | |
extensions: [ | |
new x509.KeyUsagesExtension( | |
x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment | |
), | |
], | |
attributes: [ | |
//new x509.ChallengePasswordAttribute("password"), | |
], | |
}); | |
const prv = await crypto.subtle.exportKey("pkcs8", keys.privateKey); | |
return [csr.toString("pem"), x509.PemConverter.encode(prv, "PRIVATE KEY")]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment