From 578b7d1db256d9a582cef45ae5d13d858a977416 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Thu, 26 Oct 2023 05:00:12 +0800 Subject: Add persistence Signed-off-by: Yuuta Liang --- src/main/ui/IssueScreen.java | 8 +++- src/main/ui/JCA.java | 95 ++++++++++++++++++++++++++++++-------- src/main/ui/MainScreen.java | 6 ++- src/main/ui/MgmtScreen.java | 3 ++ src/main/ui/TemplateSetScreen.java | 8 +++- src/main/ui/TemplatesScreen.java | 2 + src/main/ui/Utils.java | 6 ++- 7 files changed, 105 insertions(+), 23 deletions(-) (limited to 'src/main/ui') diff --git a/src/main/ui/IssueScreen.java b/src/main/ui/IssueScreen.java index 5e3ad50..3e70a0a 100644 --- a/src/main/ui/IssueScreen.java +++ b/src/main/ui/IssueScreen.java @@ -4,6 +4,7 @@ import model.asn1.exceptions.ParseException; import model.ca.Template; import model.csr.CertificationRequest; import model.pki.cert.Certificate; +import model.x501.Name; /** * The screen that accepts a CSR and template and allows user to change its properties and issue. @@ -65,6 +66,7 @@ public class IssueScreen implements UIHandler { public void commit() { try { Certificate certificate = session.getCa().signCert(incomingCSR.getCertificationRequestInfo(), template); + session.save(); System.out.println(Utils.toPEM(certificate.encodeDER(), "CERTIFICATE")); session.setScreen(Screen.MAIN); } catch (Throwable e) { @@ -78,7 +80,11 @@ public class IssueScreen implements UIHandler { */ private void handleIssueSetSubject(String val) { try { - template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + if (val == null) { + template = new Template(template.getName(), template.isEnabled(), (Name) null, template.getValidity()); + } else { + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + } } catch (ParseException e) { System.out.println(e.getMessage()); } diff --git a/src/main/ui/JCA.java b/src/main/ui/JCA.java index 882c546..420ec10 100644 --- a/src/main/ui/JCA.java +++ b/src/main/ui/JCA.java @@ -1,9 +1,14 @@ package ui; +import model.asn1.exceptions.InvalidDBException; import model.asn1.exceptions.ParseException; import model.ca.CertificationAuthority; +import persistence.Decoder; +import persistence.FS; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.util.Scanner; @@ -12,6 +17,11 @@ import java.util.Scanner; * Main program */ public class JCA { + /** + * Default db file (./data/ca.json) + */ + private static final Path PATH_DEFAULT = Path.of("data", "ca.json"); + /** * Instances of the five screens; */ @@ -23,12 +33,17 @@ public class JCA { /** * The CA */ - private final CertificationAuthority ca; + private CertificationAuthority ca; /** * The current screen. */ private UIHandler screen; + /** + * There are unsaved changes. + */ + private boolean unsaved = false; + /** * EFFECTS: Init with main screen and empty CA. No private key and no CA cert. * Throws {@link NoSuchAlgorithmException} when crypto issue happens. @@ -106,34 +121,72 @@ public class JCA { screen.enter(args); } + /** + * EFFECTS: Read the database file and replace all local states. + * MODIFIES: this + */ + private void load() { + if (unsaved) { + System.out.println("Current database is not saved yet."); + return; + } + try { + this.ca = Decoder.decodeCA(FS.read(PATH_DEFAULT)); + } catch (InvalidDBException | NoSuchAlgorithmException e) { + System.out.println(e.getMessage()); + if (e.getCause() != null) { + System.out.println(e.getCause().getMessage()); + e.getCause().printStackTrace(); + } + } + } + private void handleLine(String... args) { if (args[0].equals("log")) { ca.getLogs().forEach(System.out::println); return; } - switch (args[0]) { - case "help": - screen.help(); - System.out.println("log\tView audit logs"); - break; - case "show": - screen.show(); - break; - case "commit": - screen.commit(); - break; - case "exit": - setScreen(screen.exit()); - break; - default: - screen.command(args); - break; + if ("help".equals(args[0])) { + screen.help(); + System.out.println("log\tView audit logs"); + System.out.println("load\tLoad database"); + System.out.println("save\tSave database"); + } else if ("commit".equals(args[0])) { + screen.commit(); + } else if ("show".equals(args[0])) { + screen.show(); + } else if ("exit".equals(args[0])) { + setScreen(screen.exit()); + } else if ("save".equals(args[0])) { + save(); + } else if ("load".equals(args[0])) { + load(); + } else { + screen.command(args); } printPS1(); } + /** + * EFFECTS: Print the '*user@JCA PS1' line + */ private void printPS1() { - System.out.printf("%s@JCA %s ", ca.getUser(), screen.getPS1()); + System.out.printf("%s%s@JCA %s ", + unsaved ? "*" : "", + ca.getUser(), + screen.getPS1()); + } + + /** + * EFFECTS: Save the DB + */ + public void save() { + try { + FS.write(PATH_DEFAULT, Decoder.encodeCA(ca)); + unsaved = false; + } catch (IOException e) { + System.out.println(e.getMessage()); + } } /** @@ -152,6 +205,10 @@ public class JCA { } } + public void setUnsaved(boolean unsaved) { + this.unsaved = unsaved; + } + public CertificationAuthority getCa() { return ca; } diff --git a/src/main/ui/MainScreen.java b/src/main/ui/MainScreen.java index 2eaf882..8a85881 100644 --- a/src/main/ui/MainScreen.java +++ b/src/main/ui/MainScreen.java @@ -7,6 +7,7 @@ import model.asn1.parsing.BytesReader; import model.ca.Template; import model.csr.CertificationRequest; import model.pki.cert.Certificate; +import model.pki.crl.CertificateList; import model.pki.crl.Reason; import model.pki.crl.RevokedCertificate; @@ -138,6 +139,7 @@ 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.save(); } catch (IllegalArgumentException ignored) { System.out.println("Illegal serial number or reason"); } @@ -177,7 +179,9 @@ public class MainScreen implements UIHandler { return; } try { - System.out.println(Utils.toPEM(session.getCa().signCRL().encodeDER(), "X509 CRL")); + CertificateList crl = session.getCa().signCRL(); + session.save(); + System.out.println(Utils.toPEM(crl.encodeDER(), "X509 CRL")); } catch (Throwable e) { System.out.println(e.getMessage()); } diff --git a/src/main/ui/MgmtScreen.java b/src/main/ui/MgmtScreen.java index 0a25bfe..c630a34 100644 --- a/src/main/ui/MgmtScreen.java +++ b/src/main/ui/MgmtScreen.java @@ -73,6 +73,7 @@ public class MgmtScreen implements UIHandler { try { CertificationRequest req = session.getCa().signCSR(); System.out.println(Utils.toPEM(req.encodeDER(), "CERTIFICATE REQUEST")); + session.setUnsaved(true); } catch (Throwable e) { System.out.println(e.getMessage()); } @@ -90,6 +91,7 @@ public class MgmtScreen implements UIHandler { final Byte[] in = session.handleInputPEM("CERTIFICATE"); final Certificate cert = new Certificate(new BytesReader(in), false); session.getCa().installCertificate(cert); + session.setUnsaved(true); } catch (InvalidCAException | ParseException e) { System.out.println(e.getMessage()); } @@ -105,6 +107,7 @@ public class MgmtScreen implements UIHandler { } try { session.getCa().generateKey(); + session.setUnsaved(true); } catch (NoSuchAlgorithmException e) { System.out.println(e.getMessage()); } diff --git a/src/main/ui/TemplateSetScreen.java b/src/main/ui/TemplateSetScreen.java index a0b39c1..30d25b9 100644 --- a/src/main/ui/TemplateSetScreen.java +++ b/src/main/ui/TemplateSetScreen.java @@ -2,6 +2,7 @@ package ui; import model.asn1.exceptions.ParseException; import model.ca.Template; +import model.x501.Name; /** * The screen that modifies the properties of a single template and add it to the store. @@ -36,7 +37,11 @@ public class TemplateSetScreen implements UIHandler { */ private void handleSetSubject(String val) { try { - template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + if (val == null) { + template = new Template(template.getName(), template.isEnabled(), (Name) null, template.getValidity()); + } else { + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + } } catch (ParseException e) { System.out.println(e.getMessage()); } @@ -94,6 +99,7 @@ public class TemplateSetScreen implements UIHandler { @Override public void commit() { session.getCa().addTemplate(template); + session.setUnsaved(true); session.setScreen(Screen.TEMPLATES); } diff --git a/src/main/ui/TemplatesScreen.java b/src/main/ui/TemplatesScreen.java index e08df50..e622709 100644 --- a/src/main/ui/TemplatesScreen.java +++ b/src/main/ui/TemplatesScreen.java @@ -75,6 +75,7 @@ public class TemplatesScreen implements UIHandler { return; } session.getCa().setTemplateEnable(tmp, enable); + session.setUnsaved(true); } /** @@ -92,6 +93,7 @@ public class TemplatesScreen implements UIHandler { return; } session.getCa().removeTemplate(tmp); + session.setUnsaved(true); } /** diff --git a/src/main/ui/Utils.java b/src/main/ui/Utils.java index f653ffa..4a9beeb 100644 --- a/src/main/ui/Utils.java +++ b/src/main/ui/Utils.java @@ -80,7 +80,11 @@ public final class Utils { throw new ParseException("Not a valid PEM"); } final String b64 = matcher.group(1).replace("\n", ""); - return byteToByte(Base64.getDecoder().decode(b64)); + try { + return byteToByte(Base64.getDecoder().decode(b64)); + } catch (IllegalArgumentException e) { + throw new ParseException(e.getMessage()); + } } /** -- cgit v1.2.3