From 0bcc057e741af3fbc108f42b75f9d42f48f6a51e Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Sat, 14 Oct 2023 05:12:06 +0800 Subject: Implement the CA Signed-off-by: Yuuta Liang --- src/main/ui/MgmtScreen.java | 170 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/main/ui/MgmtScreen.java (limited to 'src/main/ui/MgmtScreen.java') diff --git a/src/main/ui/MgmtScreen.java b/src/main/ui/MgmtScreen.java new file mode 100644 index 0000000..613aa50 --- /dev/null +++ b/src/main/ui/MgmtScreen.java @@ -0,0 +1,170 @@ +package ui; + +import model.asn1.ASN1Object; +import model.asn1.BitString; +import model.asn1.Bool; +import model.asn1.ObjectIdentifier; +import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; +import model.csr.CertificationRequest; +import model.pki.SubjectPublicKeyInfo; +import model.pki.cert.Certificate; +import model.pki.cert.Extension; +import model.pki.cert.TbsCertificate; + +import java.util.Arrays; +import java.util.Base64; +import java.util.BitSet; + +public class MgmtScreen implements UIHandler { + private final JCA session; + + /** + * EFFECTS: Init with the parent session. + */ + public MgmtScreen(JCA session) { + this.session = session; + } + + @Override + public void help() { + System.out.print("show\tView the public key and CA certificate\n" + + "csr\tGenerate a CSR for a upper-level CA to sign\n" + + "install\tInstall a CA certificate\n" + + "exit\tGo to main menu\n" + + "help\tPrint this message\n"); + } + + /** + * EFFECTS: Format the public key and CA + */ + @Override + public void show() { + System.out.printf("Public Key:\t%s\n", + Base64.getEncoder().encodeToString(session.getCa().getPublicKey().getEncoded())); + if (!session.checkCA(true)) { + return; + } + final TbsCertificate info = session.getCa().getCertificate().getCertificate(); + System.out.printf("Subject:\t%s\n", info.getSubject().toString()); + System.out.printf("Issuer:\t%s\n", info.getIssuer().toString()); + System.out.printf("Not Before:\t%s\n", info.getValidity().getNotBefore().getTimestamp()); + System.out.printf("Not After:\t%s\n", info.getValidity().getNotAfter().getTimestamp()); + System.out.printf("Signature:\t%s\n", + Base64.getEncoder().encodeToString(Utils.byteToByte(info.getSubjectPublicKeyInfo() + .getSubjectPublicKey().getConvertedVal()))); + } + + private void handleCSR() { + if (!session.checkCA(false)) { + return; + } + try { + CertificationRequest req = session.getCa().signCSR(); + System.out.println(Utils.toPEM(req.encodeDER(), "CERTIFICATE REQUEST")); + session.log("Signed CA CSR."); + } catch (Throwable e) { + System.out.println(e.getMessage()); + } + } + + private void validateCACertificateVersion(Certificate cert) throws ParseException { + if (cert.getCertificate().getVersion() == null + || cert.getCertificate().getVersion().getLong() != TbsCertificate.VERSION_V3) { + throw new ParseException("The input certificate must be V3"); + } + } + + private void validateCACertificatePublicKey(Certificate cert) throws ParseException { + final SubjectPublicKeyInfo expectedPKInfo = session.getCa().getCAPublicKeyInfo(); + if (!Arrays.equals(cert.getCertificate().getSubjectPublicKeyInfo().getAlgorithm().getType().getInts(), + expectedPKInfo.getAlgorithm().getType().getInts()) + || !Arrays.equals(cert.getCertificate().getSubjectPublicKeyInfo().getSubjectPublicKey().getVal(), + expectedPKInfo.getSubjectPublicKey().getVal())) { + throw new ParseException("The input certificate does not have the corresponding public key"); + } + } + + private void validateCACertificateBasicConstraints(Certificate cert) throws ParseException { + final Extension basicConstraints = cert.getCertificate().getExtension(ObjectIdentifier.OID_BASIC_CONSTRAINTS); + if (basicConstraints == null + || basicConstraints.getExtnValue().getBytes().length <= 0) { + throw new ParseException("The certificate does not have a valid basicConstraints extension."); + } + final ASN1Object basicConstraintsValue = + new ASN1Object(new BytesReader(basicConstraints.getExtnValue().getBytes()), false); + if (basicConstraintsValue.getLength() <= 0) { + throw new ParseException("The certificate does not have a valid basicConstraints extension."); + } + final ASN1Object bool = + ASN1Object.parse(new BytesReader(basicConstraintsValue.encodeValueDER()), false); + if (!(bool instanceof Bool) + || !((Bool) bool).getValue()) { + throw new ParseException("The certificate does not have a valid basicConstraints extension."); + } + } + + private void validateCACertificateKeyUsage(Certificate cert) throws ParseException { + final Extension keyUsage = cert.getCertificate().getExtension(ObjectIdentifier.OID_KEY_USAGE); + if (keyUsage == null + || keyUsage.getExtnValue().getBytes().length <= 0) { + throw new ParseException("The certificate does not have a valid keyUsage extension."); + } + final ASN1Object keyUsageValue = + ASN1Object.parse(new BytesReader(keyUsage.getExtnValue().getBytes()), false); + if (keyUsageValue.getLength() <= 0 + || !(keyUsageValue instanceof BitString)) { + throw new ParseException("The certificate does not have a valid keyUsage extension."); + } + final BitSet bitSet = BitSet.valueOf(Utils.byteToByte(((BitString) keyUsageValue).getVal())); + if (!bitSet.get(7) || !bitSet.get(2) || !bitSet.get(1)) { + throw new ParseException("The certificate does not have a valid keyUsage extension."); + } + } + + private void handleInstall() { + if (!session.checkCA(false)) { + return; + } + try { + final Byte[] in = session.handleInputPEM("CERTIFICATE"); + Certificate cert = new Certificate(new BytesReader(in), false); + validateCACertificateVersion(cert); + validateCACertificatePublicKey(cert); + validateCACertificateBasicConstraints(cert); + validateCACertificateKeyUsage(cert); + session.getCa().installCertificate(cert); + session.log("A CA certificate is installed."); + } catch (ParseException e) { + System.out.println(e.getMessage()); + } + } + + @Override + public void command(String... args) { + switch (args[0]) { + case "csr": + handleCSR(); + break; + case "install": + handleInstall(); + break; + default: + help(); + break; + } + } + + /** + * EFFECTS: Go to main menu + */ + @Override + public Screen exit() { + return Screen.MAIN; + } + + @Override + public String getPS1() { + return "/ca/ #"; + } +} \ No newline at end of file -- cgit v1.2.3