Skip to content

Commit

Permalink
style: format with prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Jan 25, 2023
1 parent 18d7542 commit bb2aa4d
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 166 deletions.
6 changes: 2 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"extends": [
"eslint-config-unjs"
],
"extends": ["eslint-config-unjs"],
"rules": {
"unicorn/consistent-destructuring": 0,

"@typescript-eslint/no-non-null-assertion": 0
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint --ext .ts .",
"lint": "eslint --ext .ts . && prettier -c src test",
"play": "jiti test/fixture/app",
"release": "pnpm test && pnpm build && standard-version && git push --follow-tags && pnpm publish",
"test": "pnpm lint && vitest run --coverage"
Expand All @@ -42,6 +42,7 @@
"eslint": "^8.32.0",
"eslint-config-unjs": "^0.1.0",
"jiti": "^1.16.2",
"prettier": "^2.8.3",
"standard-version": "^9.5.0",
"typescript": "^4.9.4",
"unbuild": "^1.1.1",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 56 additions & 36 deletions src/cert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,51 @@ import forge from "node-forge";
import ipRegex from "ip-regex";

export interface Certificate {
key: string
cert: string
key: string;
cert: string;
}

// SSL Cert

export interface SSLCertOptions {
commonName?: string
domains: string[]
validityDays: number
caKey: string
caCert: string
commonName?: string;
domains: string[];
validityDays: number;
caKey: string;
caCert: string;
}

export async function generateSSLCert (options: SSLCertOptions): Promise<Certificate> {
export async function generateSSLCert(
options: SSLCertOptions
): Promise<Certificate> {
// Certificate Attributes (https://git.io/fptna)
const attributes = [
// Use the first address as common name if no common name is provided
{ name: "commonName", value: options.commonName || options.domains[0] }
{ name: "commonName", value: options.commonName || options.domains[0] },
];

// Required certificate extensions for a tls certificate
const extensions = [
{ name: "basicConstraints", cA: false, critical: true },
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true, critical: true },
{
name: "keyUsage",
digitalSignature: true,
keyEncipherment: true,
critical: true,
},
{ name: "extKeyUsage", serverAuth: true, clientAuth: true },
{
name: "subjectAltName",
altNames: options.domains.map((domain) => {
// Available Types: https://git.io/fptng
const types = { domain: 2, ip: 7 };
const isIp = ipRegex({ exact: true }).test(domain);
if (isIp) { return { type: types.ip, ip: domain }; }
if (isIp) {
return { type: types.ip, ip: domain };
}
return { type: types.domain, value: domain };
})
}
}),
},
];

const ca = forge.pki.certificateFromPem(options.caCert);
Expand All @@ -50,77 +59,88 @@ export async function generateSSLCert (options: SSLCertOptions): Promise<Certifi
issuer: ca.subject.attributes,
extensions,
validityDays: options.validityDays,
signWith: options.caKey
signWith: options.caKey,
});
}

// CA

export interface CAOptions {
commonName?: string
organization?: string
countryCode?: string
state?: string
locality?: string
validityDays?: number
commonName?: string;
organization?: string;
countryCode?: string;
state?: string;
locality?: string;
validityDays?: number;
}

export async function generateCA (options: CAOptions = {}): Promise<Certificate> {
export async function generateCA(
options: CAOptions = {}
): Promise<Certificate> {
// Certificate Attributes: https://git.io/fptna
const attributes = [
options.commonName && { name: "commonName", value: options.commonName },
options.countryCode && { name: "countryName", value: options.countryCode },
options.state && { name: "stateOrProvinceName", value: options.state },
options.locality && { name: "localityName", value: options.locality },
options.organization && { name: "organizationName", value: options.organization }
].filter(Boolean) as {name: string, value: string }[];
options.organization && {
name: "organizationName",
value: options.organization,
},
].filter(Boolean) as { name: string; value: string }[];

// Required certificate extensions for a certificate authority
const extensions = [
{ name: "basicConstraints", cA: true, critical: true },
{ name: "keyUsage", keyCertSign: true, critical: true }
{ name: "keyUsage", keyCertSign: true, critical: true },
];

return await generateCert({
subject: attributes,
issuer: attributes,
extensions,
validityDays: options.validityDays || 365
validityDays: options.validityDays || 365,
});
}

// Cert

interface CertOptions {
subject: forge.pki.CertificateField[]
issuer: forge.pki.CertificateField[]
extensions: any[]
validityDays: number
signWith?: string
subject: forge.pki.CertificateField[];
issuer: forge.pki.CertificateField[];
extensions: any[];
validityDays: number;
signWith?: string;
}

export async function generateCert (options: CertOptions): Promise<Certificate> {
export async function generateCert(options: CertOptions): Promise<Certificate> {
// Create serial from and integer between 50000 and 99999
const serial = Math.floor((Math.random() * 95_000) + 50_000).toString();
const generateKeyPair = promisify(forge.pki.rsa.generateKeyPair.bind(forge.pki.rsa));
const serial = Math.floor(Math.random() * 95_000 + 50_000).toString();
const generateKeyPair = promisify(
forge.pki.rsa.generateKeyPair.bind(forge.pki.rsa)
);
const keyPair = await generateKeyPair({ bits: 2048, workers: 4 });
const cert = forge.pki.createCertificate();

cert.publicKey = keyPair.publicKey;
cert.serialNumber = Buffer.from(serial).toString("hex"); // serial number must be hex encoded
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() + options.validityDays);
cert.validity.notAfter.setDate(
cert.validity.notAfter.getDate() + options.validityDays
);
cert.setSubject(options.subject);
cert.setIssuer(options.issuer);
cert.setExtensions(options.extensions);

// Sign the certificate with it's own private key if no separate signing key is provided
const signWith = options.signWith ? forge.pki.privateKeyFromPem(options.signWith) : keyPair.privateKey;
const signWith = options.signWith
? forge.pki.privateKeyFromPem(options.signWith)
: keyPair.privateKey;
cert.sign(signWith, forge.md.sha256.create());

return {
key: forge.pki.privateKeyToPem(keyPair.privateKey),
cert: forge.pki.certificateToPem(cert)
cert: forge.pki.certificateToPem(cert),
};
}

0 comments on commit bb2aa4d

Please sign in to comment.