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; /** * 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; */ private final UIHandler mainScreen; private final UIHandler mgmtScreen; private final UIHandler issueScreen; private final UIHandler templatesScreen; private final UIHandler templateSetScreen; /** * The 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. */ public JCA() throws NoSuchAlgorithmException, InvalidKeySpecException { this.mainScreen = new MainScreen(this); this.mgmtScreen = new MgmtScreen(this); this.issueScreen = new IssueScreen(this); this.templatesScreen = new TemplatesScreen(this); this.templateSetScreen = new TemplateSetScreen(this); setScreen(Screen.MAIN); this.ca = new CertificationAuthority(); } /** * EFFECT: Checks if the CA is installed or not (according to the desired state) and print if not matching. Returns * true if matching. */ public boolean checkCA(boolean requireInstalled) { if (requireInstalled && ca.getCertificate() == null) { System.out.println("No CA installed"); return false; } else if (!requireInstalled && ca.getCertificate() != null) { System.out.println("CA already installed"); return false; } return true; } /** * EFFECTS: Read PEM from stdin, matched the given tag. * Throws {@link ParseException} if the input is incorrect. */ public Byte[] handleInputPEM(String desiredTag) throws ParseException { final Scanner scanner = new Scanner(System.in); StringBuilder in = new StringBuilder(); while (true) { final String line = scanner.nextLine(); in.append(line); in.append("\n"); if (line.matches("-----END .*-----")) { break; } } return Utils.parsePEM(Utils.byteToByte(in.toString().getBytes(StandardCharsets.UTF_8)), desiredTag); } /** * EFFECT: Set the current screen with optional args. Exit the program when mode is null. * MODIFIES: this */ public void setScreen(Screen mode, Object... args) { if (mode == null) { System.exit(0); } switch (mode) { case MAIN: this.screen = mainScreen; break; case MGMT: this.screen = mgmtScreen; break; case ISSUE: this.screen = issueScreen; break; case TEMPLATES: this.screen = templatesScreen; break; case TEMPLATE_SET: this.screen = templateSetScreen; break; } 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(); } } } /** * EFFECTS: Handle input line * MODIFIES: this */ private void handleLine(String... args) { if (args[0].equals("log")) { ca.getLogs().forEach(System.out::println); return; } 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%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()); } } /** * EFFECTS: Run the program */ public void run() { printPS1(); final Scanner scanner = new Scanner(System.in); while (true) { String[] args = scanner.nextLine().split(" "); if (args.length <= 0 || args[0].isBlank()) { printPS1(); continue; } handleLine(args); } } public void setUnsaved(boolean unsaved) { this.unsaved = unsaved; } public CertificationAuthority getCa() { return ca; } }