diff options
author | Yuuta Liang <yuutaw@student.cs.ubc.ca> | 2023-10-25 03:30:45 +0800 |
---|---|---|
committer | Yuuta Liang <yuutaw@student.cs.ubc.ca> | 2023-10-25 03:30:45 +0800 |
commit | d7ff9d5e217873609d79efe279f2634e3a3dd8b4 (patch) | |
tree | 704729e5eed658728b521acd407c6ca767f7e865 /src/main/ui | |
parent | 55df54e5dbf26e6824123410784d00aa793c3781 (diff) | |
download | jca-d7ff9d5e217873609d79efe279f2634e3a3dd8b4.tar jca-d7ff9d5e217873609d79efe279f2634e3a3dd8b4.tar.gz jca-d7ff9d5e217873609d79efe279f2634e3a3dd8b4.tar.bz2 jca-d7ff9d5e217873609d79efe279f2634e3a3dd8b4.zip |
Refactor: move all logics into CertificationAuthority
Signed-off-by: Yuuta Liang <yuutaw@student.cs.ubc.ca>
Diffstat (limited to 'src/main/ui')
-rw-r--r-- | src/main/ui/IssueScreen.java | 22 | ||||
-rw-r--r-- | src/main/ui/JCA.java | 73 | ||||
-rw-r--r-- | src/main/ui/MainScreen.java | 4 | ||||
-rw-r--r-- | src/main/ui/MgmtScreen.java | 117 | ||||
-rw-r--r-- | src/main/ui/TemplateSetScreen.java | 13 | ||||
-rw-r--r-- | src/main/ui/TemplatesScreen.java | 17 | ||||
-rw-r--r-- | src/main/ui/Utils.java | 14 |
7 files changed, 73 insertions, 187 deletions
diff --git a/src/main/ui/IssueScreen.java b/src/main/ui/IssueScreen.java index 8376146..5e3ad50 100644 --- a/src/main/ui/IssueScreen.java +++ b/src/main/ui/IssueScreen.java @@ -66,7 +66,6 @@ public class IssueScreen implements UIHandler { try { Certificate certificate = session.getCa().signCert(incomingCSR.getCertificationRequestInfo(), template); System.out.println(Utils.toPEM(certificate.encodeDER(), "CERTIFICATE")); - session.log("A certificate was issued."); session.setScreen(Screen.MAIN); } catch (Throwable e) { System.out.println(e.getMessage()); @@ -75,11 +74,11 @@ public class IssueScreen implements UIHandler { /** * EFFECTS: Set or unset the subject. - * MODIFIES: template + * MODIFIES: this */ private void handleIssueSetSubject(String val) { try { - template.setSubject(val); + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); } catch (ParseException e) { System.out.println(e.getMessage()); } @@ -87,7 +86,7 @@ public class IssueScreen implements UIHandler { /** * EFFECTS: Set or unset the validity. - * MODIFIES: template + * MODIFIES: this */ private void handleIssueSetValidity(String val) { if (val == null) { @@ -100,7 +99,7 @@ public class IssueScreen implements UIHandler { System.out.println("Invalid validity days"); return; } - template.setValidity(i); + template = new Template(template.getName(), template.isEnabled(), template.getSubject(), i); } catch (NumberFormatException ignored) { System.out.println("Invalid validity days"); } @@ -108,7 +107,7 @@ public class IssueScreen implements UIHandler { /** * EFFECTS: Handle the set command. - * MODIFIES: template + * MODIFIES: this */ private void handleIssueSet(String... args) { if (args.length != 2 && args.length != 3) { @@ -132,13 +131,10 @@ public class IssueScreen implements UIHandler { @Override public void command(String... args) { - switch (args[0]) { - case "set": - handleIssueSet(args); - break; - default: - help(); - break; + if (args[0].equals("set")) { + handleIssueSet(args); + } else { + help(); } } diff --git a/src/main/ui/JCA.java b/src/main/ui/JCA.java index 7892850..882c546 100644 --- a/src/main/ui/JCA.java +++ b/src/main/ui/JCA.java @@ -1,16 +1,11 @@ package ui; import model.asn1.exceptions.ParseException; -import model.ca.AuditLogEntry; -import model.ca.CACertificate; -import model.ca.Template; +import model.ca.CertificationAuthority; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.security.spec.InvalidKeySpecException; import java.util.Scanner; /** @@ -26,31 +21,19 @@ public class JCA { private final UIHandler templatesScreen; private final UIHandler templateSetScreen; /** - * Templates - */ - private final List<Template> templates; - /** * The CA */ - private final CACertificate ca; - /** - * Audit logs - */ - private final List<AuditLogEntry> logs; - /** - * Current user - */ - private final String user; + private final CertificationAuthority ca; /** * The current screen. */ private UIHandler screen; /** - * EFFECTS: Init with main screen, empty templates, logs, user 'yuuta', and generate a private key with no CA cert. + * EFFECTS: Init with main screen and empty CA. No private key and no CA cert. * Throws {@link NoSuchAlgorithmException} when crypto issue happens. */ - public JCA() throws NoSuchAlgorithmException { + public JCA() throws NoSuchAlgorithmException, InvalidKeySpecException { this.mainScreen = new MainScreen(this); this.mgmtScreen = new MgmtScreen(this); this.issueScreen = new IssueScreen(this); @@ -59,12 +42,7 @@ public class JCA { setScreen(Screen.MAIN); - this.templates = new ArrayList<>(); - this.ca = new CACertificate(); - this.logs = new ArrayList<>(); - this.user = "yuuta"; - - this.ca.generateKey(); + this.ca = new CertificationAuthority(); } /** @@ -73,10 +51,10 @@ public class JCA { */ public boolean checkCA(boolean requireInstalled) { if (requireInstalled && ca.getCertificate() == null) { - System.out.println("The CA is not installed yet"); + System.out.println("No CA installed"); return false; } else if (!requireInstalled && ca.getCertificate() != null) { - System.out.println("The CA is already installed"); + System.out.println("CA already installed"); return false; } return true; @@ -101,19 +79,6 @@ public class JCA { } /** - * EFFECTS: Find the template based on name, or null if not found. - */ - public Template findTemplate(String name, boolean requireEnabled) { - Optional<Template> opt = templates.stream().filter(temp -> { - if (requireEnabled && !temp.isEnabled()) { - return false; - } - return temp.getName().equals(name); - }).findFirst(); - return opt.orElse(null); - } - - /** * EFFECT: Set the current screen with optional args. Exit the program when mode is null. * MODIFIES: this */ @@ -143,7 +108,7 @@ public class JCA { private void handleLine(String... args) { if (args[0].equals("log")) { - getLogs().forEach(System.out::println); + ca.getLogs().forEach(System.out::println); return; } switch (args[0]) { @@ -168,15 +133,7 @@ public class JCA { } private void printPS1() { - System.out.printf("%s@JCA %s ", user, screen.getPS1()); - } - - /** - * EFFECT: Log an action to the audit log - * MODIFIES: this - */ - public void log(String action) { - this.logs.add(new AuditLogEntry(user, ZonedDateTime.now(), action)); + System.out.printf("%s@JCA %s ", ca.getUser(), screen.getPS1()); } /** @@ -195,15 +152,7 @@ public class JCA { } } - public List<Template> getTemplates() { - return templates; - } - - public CACertificate getCa() { + public CertificationAuthority getCa() { return ca; } - - public List<AuditLogEntry> getLogs() { - return logs; - } } diff --git a/src/main/ui/MainScreen.java b/src/main/ui/MainScreen.java index b6e4372..2eaf882 100644 --- a/src/main/ui/MainScreen.java +++ b/src/main/ui/MainScreen.java @@ -86,7 +86,7 @@ public class MainScreen implements UIHandler { System.out.println("Usage: issue <template>"); return; } - Template tmp = session.findTemplate(args[1], true); + Template tmp = session.getCa().findTemplate(args[1], true); if (tmp == null) { System.out.println("Cannot find the template specified"); return; @@ -138,7 +138,6 @@ public class MainScreen implements UIHandler { session.getCa().revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, c.getCertificate().getSerialNumber(), new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), reason)); - session.log("A certificate has been revoked."); } catch (IllegalArgumentException ignored) { System.out.println("Illegal serial number or reason"); } @@ -179,7 +178,6 @@ public class MainScreen implements UIHandler { } try { System.out.println(Utils.toPEM(session.getCa().signCRL().encodeDER(), "X509 CRL")); - session.log("A CRL was signed"); } catch (Throwable e) { System.out.println(e.getMessage()); } diff --git a/src/main/ui/MgmtScreen.java b/src/main/ui/MgmtScreen.java index 1957c7e..0a25bfe 100644 --- a/src/main/ui/MgmtScreen.java +++ b/src/main/ui/MgmtScreen.java @@ -1,20 +1,14 @@ package ui; -import model.asn1.ASN1Object; -import model.asn1.BitString; -import model.asn1.Bool; -import model.asn1.ObjectIdentifier; +import model.asn1.exceptions.InvalidCAException; 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.security.NoSuchAlgorithmException; import java.util.Base64; -import java.util.BitSet; /** * Manage the private key and CA certificate. It can print the public key, generate CSR, and install CA cert. @@ -35,6 +29,7 @@ public class MgmtScreen implements UIHandler { @Override public void help() { System.out.print("show\tView the public key and CA certificate\n" + + "genkey\tGenerate a RSA private key\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" @@ -46,8 +41,14 @@ public class MgmtScreen implements UIHandler { */ @Override public void show() { - System.out.printf("Public Key:\t%s\n", - Base64.getEncoder().encodeToString(session.getCa().getPublicKey().getEncoded())); + if (session.getCa().getPublicKey() == null) { + System.out.println("No private key installed"); + } else { + System.out.println("Public Key (RSA2048):"); + System.out.printf("\tModules:\t\t%s\n", session.getCa().getPublicKey().getModulus().toString(10)); + System.out.printf("\tPublic Exponent:\t%s\n", + session.getCa().getPublicKey().getPublicExponent().toString(16)); + } if (!session.checkCA(true)) { return; } @@ -63,6 +64,7 @@ public class MgmtScreen implements UIHandler { /** * EFFECT: Generate a CSR + * MODIFIES: session */ private void handleCSR() { if (!session.checkCA(false)) { @@ -71,80 +73,14 @@ public class MgmtScreen implements UIHandler { 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()); } } /** - * EFFECTS: Throw {@link ParseException} if the incoming cert is not v3. - */ - 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"); - } - } - - /** - * EFFECTS: Throw {@link ParseException} if the incoming cert does not have the matching public key. - */ - 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"); - } - } - - /** - * EFFECTS: Throw {@link ParseException} if the incoming cert does not have cA = true in its basicConstraints. - */ - 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."); - } - } - - /** - * EFFECTS: Throw {@link ParseException} if the incoming cert does not have valid key usages. - */ - 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."); - } - } - - /** * EFFECTS: Handle the 'install' command. Read incoming certificate and validate it. + * MODIFIES: session */ private void handleInstall() { if (!session.checkCA(false)) { @@ -152,14 +88,24 @@ public class MgmtScreen implements UIHandler { } try { final Byte[] in = session.handleInputPEM("CERTIFICATE"); - Certificate cert = new Certificate(new BytesReader(in), false); - validateCACertificateVersion(cert); - validateCACertificatePublicKey(cert); - validateCACertificateBasicConstraints(cert); - validateCACertificateKeyUsage(cert); + final Certificate cert = new Certificate(new BytesReader(in), false); session.getCa().installCertificate(cert); - session.log("A CA certificate is installed."); - } catch (ParseException e) { + } catch (InvalidCAException | ParseException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Handle the 'genkey' command. Generate a RSA2048 private key. + * MODIFIES: session + */ + private void handleGenKey() { + if (session.getCa().getPublicKey() != null) { + System.out.println("A private key is already installed."); + } + try { + session.getCa().generateKey(); + } catch (NoSuchAlgorithmException e) { System.out.println(e.getMessage()); } } @@ -170,6 +116,9 @@ public class MgmtScreen implements UIHandler { @Override public void command(String... args) { switch (args[0]) { + case "genkey": + handleGenKey(); + break; case "csr": handleCSR(); break; diff --git a/src/main/ui/TemplateSetScreen.java b/src/main/ui/TemplateSetScreen.java index 42f393b..a0b39c1 100644 --- a/src/main/ui/TemplateSetScreen.java +++ b/src/main/ui/TemplateSetScreen.java @@ -32,11 +32,11 @@ public class TemplateSetScreen implements UIHandler { /** * EFFECTS: Parse and set / unset the subject of the template - * MODIFIES: this#template + * MODIFIES: this */ private void handleSetSubject(String val) { try { - template.setSubject(val); + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); } catch (ParseException e) { System.out.println(e.getMessage()); } @@ -44,7 +44,7 @@ public class TemplateSetScreen implements UIHandler { /** * EFFECTS: Set the validity of the template to the given integer - * MODIFIES: this#template + * MODIFIES: this */ private void handleSetValidity(String val) { if (val == null) { @@ -57,7 +57,7 @@ public class TemplateSetScreen implements UIHandler { System.out.println("Invalid validity days"); return; } - template.setValidity(i); + template = new Template(template.getName(), template.isEnabled(), template.getSubject(), i); } catch (NumberFormatException ignored) { System.out.println("Invalid validity days"); } @@ -65,7 +65,7 @@ public class TemplateSetScreen implements UIHandler { /** * EFFECTS: Handle the `set` command. - * MODIFIES: this#template + * MODIFIES: this */ private void handleSet(String... args) { if (args.length != 2 && args.length != 3) { @@ -93,9 +93,8 @@ public class TemplateSetScreen implements UIHandler { */ @Override public void commit() { - session.getTemplates().add(template); + session.getCa().addTemplate(template); session.setScreen(Screen.TEMPLATES); - session.log("A new template is added."); } /** diff --git a/src/main/ui/TemplatesScreen.java b/src/main/ui/TemplatesScreen.java index 3bdbebe..e08df50 100644 --- a/src/main/ui/TemplatesScreen.java +++ b/src/main/ui/TemplatesScreen.java @@ -1,6 +1,7 @@ package ui; import model.ca.Template; +import model.x501.Name; /** * The screen that allows users to list templates and manage them. @@ -34,7 +35,7 @@ public class TemplatesScreen implements UIHandler { */ @Override public void show() { - session.getTemplates().forEach(tem -> + session.getCa().getTemplates().forEach(tem -> System.out.printf("%s[%s]\t%s\t%d Days\n", tem.getName(), tem.isEnabled() ? "ENABLED" : "DISABLED", @@ -50,13 +51,13 @@ public class TemplatesScreen implements UIHandler { System.out.println("Usage: add <name>"); return; } - if (session.findTemplate(args[1], false) != null) { + if (session.getCa().findTemplate(args[1], false) != null) { System.out.println("The template already exists."); return; } session.setScreen(Screen.TEMPLATE_SET, - new Template(args[1], false, null, 30)); + new Template(args[1], false, (Name) null, 30)); } /** @@ -68,13 +69,12 @@ public class TemplatesScreen implements UIHandler { System.out.printf("Usage: %s <template>\n", enable ? "enable" : "disable"); return; } - Template tmp = session.findTemplate(args[1], false); + Template tmp = session.getCa().findTemplate(args[1], false); if (tmp == null) { System.out.println("Cannot find the template specified"); return; } - tmp.setEnabled(enable); - session.log("A template was enabled / disabled."); + session.getCa().setTemplateEnable(tmp, enable); } /** @@ -86,13 +86,12 @@ public class TemplatesScreen implements UIHandler { System.out.println("Usage: delete <template>"); return; } - Template tmp = session.findTemplate(args[1], true); + Template tmp = session.getCa().findTemplate(args[1], true); if (tmp == null) { System.out.println("Cannot find the template specified"); return; } - session.getTemplates().remove(tmp); - session.log("A template was deleted."); + session.getCa().removeTemplate(tmp); } /** diff --git a/src/main/ui/Utils.java b/src/main/ui/Utils.java index 6841536..f653ffa 100644 --- a/src/main/ui/Utils.java +++ b/src/main/ui/Utils.java @@ -88,14 +88,10 @@ public final class Utils { * REQUIRES: desiredTag must be upper case and not empty. */ public static String toPEM(Byte[] input, String tag) { - StringBuilder builder = new StringBuilder(); - builder.append("-----BEGIN "); - builder.append(tag); - builder.append("-----\n"); - builder.append(Base64.getEncoder().encodeToString(byteToByte(input))); - builder.append("\n-----END "); - builder.append(tag); - builder.append("-----"); - return builder.toString(); + return "-----BEGIN " + tag + "-----\n" + + Base64.getEncoder().encodeToString(byteToByte(input)) + + "\n-----END " + + tag + + "-----"; } } |