diff --git a/src/tools/csr-generator/csr-generator.service.ts b/src/tools/csr-generator/csr-generator.service.ts new file mode 100644 index 00000000..040b6221 --- /dev/null +++ b/src/tools/csr-generator/csr-generator.service.ts @@ -0,0 +1,77 @@ +import { pki } from 'node-forge'; +import workerScript from 'node-forge/dist/prime.worker.min?url'; + +export { generateCSR }; + +function generateRSAPairs({ bits = 2048 }) { + return new Promise((resolve, reject) => + pki.rsa.generateKeyPair({ bits, workerScript }, (err, keyPair) => { + if (err) { + reject(err); + return; + } + + resolve(keyPair); + }), + ); +} + +async function generateCSR(config: { + bits?: number + password?: string + commonName?: string + countryName?: string + city?: string + state?: string + organizationName?: string + organizationalUnit?: string + contactEmail?: string +} = {}): Promise<{ + publicKeyPem: string + privateKeyPem: string + csrPem: string +}> { + const { privateKey, publicKey } = await generateRSAPairs(config); + + // create a certification request (CSR) + const csr = pki.createCertificationRequest(); + csr.publicKey = publicKey; + csr.setSubject([{ + name: 'CN', + value: config.commonName, + }, { + name: 'C', + value: config.countryName, + }, { + shortName: 'ST', + value: config.state, + }, { + name: 'L', + value: config.city, + }, { + name: 'O', + value: config.organizationName, + }, { + shortName: 'OU', + value: config.organizationalUnit, + }, { + name: 'EMAIL', + value: config.contactEmail, + }]); + + // sign certification request + csr.sign(privateKey); + + // convert certification request to PEM-format + const csrPem = pki.certificationRequestToPem(csr); + + const privateUnencryptedKeyPem = pki.privateKeyToPem(privateKey); + + return { + csrPem, + publicKeyPem: pki.publicKeyToPem(publicKey), + privateKeyPem: config?.password + ? pki.encryptRsaPrivateKey(privateKey, config?.password) + : privateUnencryptedKeyPem, + }; +} diff --git a/src/tools/csr-generator/csr-generator.vue b/src/tools/csr-generator/csr-generator.vue new file mode 100644 index 00000000..8b10d9af --- /dev/null +++ b/src/tools/csr-generator/csr-generator.vue @@ -0,0 +1,130 @@ + + + diff --git a/src/tools/csr-generator/index.ts b/src/tools/csr-generator/index.ts new file mode 100644 index 00000000..e17d4f7a --- /dev/null +++ b/src/tools/csr-generator/index.ts @@ -0,0 +1,12 @@ +import { ArrowsShuffle } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'Csr generator', + path: '/csr-generator', + description: '', + keywords: ['csr', 'certificate', 'signing', 'request', 'x509', 'generator'], + component: () => import('./csr-generator.vue'), + icon: ArrowsShuffle, + createdAt: new Date('2024-02-22'), +}); diff --git a/src/tools/index.ts b/src/tools/index.ts index 2a477ed2..ca75c83e 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; import { tool as textToUnicode } from './text-to-unicode'; +import { tool as csrGenerator } from './csr-generator'; import { tool as pdfSignatureChecker } from './pdf-signature-checker'; import { tool as numeronymGenerator } from './numeronym-generator'; import { tool as macAddressGenerator } from './mac-address-generator'; @@ -81,7 +82,20 @@ import { tool as yamlViewer } from './yaml-viewer'; export const toolsByCategory: ToolCategory[] = [ { name: 'Crypto', - components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker], + components: [ + tokenGenerator, + hashText, + bcrypt, + uuidGenerator, + ulidGenerator, + cypher, + bip39, + hmacGenerator, + rsaKeyPairGenerator, + csrGenerator, + passwordStrengthAnalyser, + pdfSignatureChecker, + ], }, { name: 'Converter',