1import { 2 generateKeyPair, 3 generateSelfSignedCodeSigningCertificate, 4 convertCertificateToCertificatePEM, 5 convertKeyPairToPEM, 6} from '@expo/code-signing-certificates'; 7import assert from 'assert'; 8import { promises as fs } from 'fs'; 9import { ensureDir } from 'fs-extra'; 10import path from 'path'; 11 12import { log } from './utils/log'; 13 14type Options = { output?: string; validityDurationYears?: number; commonName?: string }; 15 16export async function generateCodeSigningAsync( 17 projectRoot: string, 18 { output, validityDurationYears: validityDurationYearsNumber, commonName }: Options 19) { 20 assert(typeof output === 'string', '--output must be a string'); 21 assert( 22 typeof validityDurationYearsNumber === 'number', 23 '--validity-duration-years must be a number' 24 ); 25 assert(typeof commonName === 'string', '--common-name must be a string'); 26 27 const validityDurationYears = Math.floor(validityDurationYearsNumber); 28 29 const outputDir = path.resolve(projectRoot, output); 30 await ensureDir(outputDir); 31 32 const isDirectoryEmpty = (await fs.readdir(outputDir)).length === 0; 33 assert(isDirectoryEmpty, 'Output directory must be empty'); 34 35 const keyPair = generateKeyPair(); 36 const validityNotBefore = new Date(); 37 const validityNotAfter = new Date(); 38 validityNotAfter.setFullYear(validityNotAfter.getFullYear() + validityDurationYears); 39 const certificate = generateSelfSignedCodeSigningCertificate({ 40 keyPair, 41 validityNotBefore, 42 validityNotAfter, 43 commonName, 44 }); 45 46 const keyPairPEM = convertKeyPairToPEM(keyPair); 47 const certificatePEM = convertCertificateToCertificatePEM(certificate); 48 49 await Promise.all([ 50 fs.writeFile(path.join(outputDir, 'public-key.pem'), keyPairPEM.publicKeyPEM), 51 fs.writeFile(path.join(outputDir, 'private-key.pem'), keyPairPEM.privateKeyPEM), 52 fs.writeFile(path.join(outputDir, 'certificate.pem'), certificatePEM), 53 ]); 54 55 log(`Generated keys and certificates output to ${outputDir}`); 56} 57