From 37d3951d03bfb9e7f3181b03c6895fd59a935227 Mon Sep 17 00:00:00 2001 From: microshine Date: Thu, 30 May 2024 13:01:13 +0200 Subject: [PATCH] feat: Add certificate profile selection in CA issue certificate view --- src/CaIssueCertificateView.tsx | 25 +++++++++++++++++++------ src/CaProvider.tsx | 34 ++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/CaIssueCertificateView.tsx b/src/CaIssueCertificateView.tsx index efcbd80..5a343fc 100644 --- a/src/CaIssueCertificateView.tsx +++ b/src/CaIssueCertificateView.tsx @@ -1,10 +1,10 @@ -import { Box, Button, List, ListItem, Step, StepLabel, Stepper, TextField, Typography } from "@mui/material"; +import { Box, Button, FormControl, InputLabel, List, ListItem, MenuItem, Select, Step, StepLabel, Stepper, TextField, Typography } from "@mui/material"; import * as React from 'react'; import { Pkcs10CertificateRequest, X509Certificate } from "@peculiar/x509"; import { Convert } from "pvtsutils"; import { CertificateDetails } from "./CertificateDetails"; -import { useCaContext } from "./CaProvider"; +import { CertificateProfile, useCaContext } from "./CaProvider"; import { useApplicationContext } from "./AppProvider"; export interface CaIssueCertificateViewProps { @@ -21,6 +21,7 @@ export const CaIssueCertificateView: React.FC = () const fileInputRef = React.useRef(null); const [file, setFile] = React.useState(null); const [isDragOver, setIsDragOver] = React.useState(false); + const [profile, setProfile] = React.useState('none'); const handleDragEnter = (e: React.DragEvent) => { e.preventDefault(); @@ -102,7 +103,8 @@ export const CaIssueCertificateView: React.FC = () (async () => { const issuedCert = await enrollCertificate(csr, { subject: certName, - validity: certValidity + validity: certValidity, + profile, }); setCert(issuedCert.toString()); setActiveStep(2); @@ -153,7 +155,7 @@ export const CaIssueCertificateView: React.FC = () { - activeStep === 0 && + activeStep === 0 && // Import CSR ( @@ -193,7 +195,7 @@ export const CaIssueCertificateView: React.FC = () ) } { - activeStep === 1 && + activeStep === 1 && // Issue Certificate ( Please enter the certificate details below: @@ -204,6 +206,17 @@ export const CaIssueCertificateView: React.FC = () + + + Profile + + + @@ -213,7 +226,7 @@ export const CaIssueCertificateView: React.FC = () ) } { - activeStep === 2 && + activeStep === 2 && // Done ( Certificate issued successfully. diff --git a/src/CaProvider.tsx b/src/CaProvider.tsx index 2f0a73b..cc561b1 100644 --- a/src/CaProvider.tsx +++ b/src/CaProvider.tsx @@ -70,9 +70,12 @@ export class CaDataBase { } } +export type CertificateProfile = 'none' | 'code_signing' | 'smime' | 'pdf_signing'; + export interface CaEnrolParams { subject: string; validity: number; + profile: CertificateProfile; } export interface CaContextProps { @@ -198,7 +201,8 @@ export const CaProvider: React.FC = (params) => { if (serial[0] === 0) { serial[1] |= 0x80; } - const cert = await x509.X509CertificateGenerator.create({ + + const certParams: x509.X509CertificateCreateParams = { serialNumber: Convert.ToHex(serial), subject: params.subject, issuer: caCert.subject, @@ -212,14 +216,28 @@ export const CaProvider: React.FC = (params) => { signingKey: value.key, extensions: [ new x509.BasicConstraintsExtension(false, undefined, true), - new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment, true), - await x509.AuthorityKeyIdentifierExtension.create(caCert), - await x509.SubjectKeyIdentifierExtension.create(req.publicKey), - new x509.ExtendedKeyUsageExtension([ - x509.ExtendedKeyUsage.codeSigning, - ]) ], - }); + }; + const extensions = certParams.extensions || []; + switch (params.profile) { + case 'code_signing': + extensions.push(new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true)); + extensions.push(new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage.codeSigning])); + break; + case 'smime': + extensions.push(new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.dataEncipherment | x509.KeyUsageFlags.keyEncipherment, true)); + extensions.push(new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage.emailProtection, x509.ExtendedKeyUsage.clientAuth])); + break; + case 'pdf_signing': + extensions.push(new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true)); + extensions.push(new x509.ExtendedKeyUsageExtension(['1.2.840.113583.1.1.10'])); // Adobe PDF + break; + default: + extensions.push(new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true)); + break; + } + + const cert = await x509.X509CertificateGenerator.create(certParams); return cert; },