From 65ea6c17a0c1348aa9ef4e158102ddf173936882 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Thu, 23 Nov 2023 08:09:01 +0800 Subject: Add GUI Signed-off-by: Yuuta Liang --- src/main/model/ca/CertificationAuthority.java | 94 ++++++++++++++++++++------- 1 file changed, 69 insertions(+), 25 deletions(-) (limited to 'src/main/model/ca/CertificationAuthority.java') diff --git a/src/main/model/ca/CertificationAuthority.java b/src/main/model/ca/CertificationAuthority.java index 038d209..5181f1a 100644 --- a/src/main/model/ca/CertificationAuthority.java +++ b/src/main/model/ca/CertificationAuthority.java @@ -1,5 +1,6 @@ package model.ca; +import model.Observer; import model.asn1.*; import model.asn1.exceptions.InvalidCAException; import model.asn1.exceptions.ParseException; @@ -28,11 +29,12 @@ import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Stream; /** - * Holds a CA private key, its certificate, signed / revoked list, template list, and logs list. + * Holds a CA private key, its certificate, signed / revoked list, template list, and logs list. Data can be observed. */ public class CertificationAuthority { public static final int SERIAL_DEFAULT = 1; @@ -83,7 +85,12 @@ public class CertificationAuthority { private final String user; /** - * EFFECT: Init with the given parameters and user "yuuta". + * Data observers. + */ + private final List observers; + + /** + * EFFECT: Init with the given parameters, user "yuuta", and no observers. * Throws {@link NoSuchAlgorithmException} if the key is specified but RSA is not supported. * Throws {@link InvalidKeySpecException} if the key specified is invalid. * Throws {@link InvalidCAException} or {@link ParseException} if the CA specified is invalid. @@ -111,11 +118,12 @@ public class CertificationAuthority { this.templates = new ArrayList<>(templates); this.logs = new ArrayList<>(logs); this.user = "yuuta"; + this.observers = new ArrayList<>(); } /** * EFFECT: Init with a null key and null certificate, empty signed, revoked template, and log list, - * serial at SERIAL_DEFAULT, and user "yuuta". + * serial at SERIAL_DEFAULT, user "yuuta", and no observers. */ public CertificationAuthority() { this.key = null; @@ -127,10 +135,13 @@ public class CertificationAuthority { this.templates = new ArrayList<>(); this.logs = new ArrayList<>(); this.user = "yuuta"; + this.observers = new ArrayList<>(); } /** * EFFECTS: Generate a new RSA2048 private key. This action will be logged. + * Observers will be notified for (RSAPublicKey.class, DIRECTION_CHANGE, INDEX_NOT_IN_LIST). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: getPublicKey() is null (i.e., no private key had been installed) * MODIFIES: this */ @@ -140,6 +151,7 @@ public class CertificationAuthority { final KeyPair pair = gen.generateKeyPair(); this.key = (RSAPrivateKey) pair.getPrivate(); this.publicKey = (RSAPublicKey) pair.getPublic(); + notif(getPublicKey(), Observer.DIRECTION_CHANGE, Observer.INDEX_NOT_IN_LIST); log("Generated CA private key."); } @@ -159,6 +171,8 @@ public class CertificationAuthority { /** * EFFECTS: Load the RSA private and public exponents. This action will be logged. + * Observers will be notified for (RSAPublicKey.class, DIRECTION_CHANGE, INDEX_NOT_IN_LIST). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * Throws {@link NoSuchAlgorithmException} if RSA is not available on the platform. * Throws {@link InvalidKeySpecException} if the input is invalid. * REQUIRES: getPublicKey() is null (i.e., no private key had been installed) @@ -167,6 +181,7 @@ public class CertificationAuthority { public void loadKey(BigInteger n, BigInteger p, BigInteger e) throws NoSuchAlgorithmException, InvalidKeySpecException { setKey(n, p, e); + notif(getPublicKey(), Observer.DIRECTION_CHANGE, Observer.INDEX_NOT_IN_LIST); log("Installed CA private key."); } @@ -252,6 +267,8 @@ public class CertificationAuthority { * - The new certificate must have the same algorithm and public key as getPublicKey() * - It must have basicConstraints { cA = TRUE } * - It must contain key usage Digital Signature, Certificate Sign, CRL Sign + * Observers will be notified for (Certificate.class, DIRECTION_CHANGE, INDEX_NOT_IN_LIST). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * Throws {@link ParseException} if the cert has invalid extension values. * This action will be logged. * REQUIRES: @@ -261,6 +278,7 @@ public class CertificationAuthority { public void installCertificate(Certificate certificate) throws InvalidCAException, ParseException { validateCertificate(certificate); this.certificate = certificate; + notif(certificate, Observer.DIRECTION_CHANGE, Observer.INDEX_NOT_IN_LIST); log("CA certificate is installed."); } @@ -325,6 +343,7 @@ public class CertificationAuthority { /** * EFFECT: Generate CSR and sign it, so the CA can request itself a certificate. + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: The CA cert must not be installed. * MODIFIES: this (This action will be logged) */ @@ -351,6 +370,8 @@ public class CertificationAuthority { /** * EFFECTS: Sign the CSR based on the template. + * Observers will be notified for (Certificate.class, DIRECTION_ADD, i). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: The CA cert must be installed first, req must have a subject, template must be enabled. * MODIFIES: this */ @@ -363,7 +384,8 @@ public class CertificationAuthority { new BitString(BitString.TAG, null, 0, signBytes(newCert.encodeValueDER()))); this.signed.add(cert); - log("Signed a cert with serial number " + cert.getCertificate().getSerialNumber()); + notif(cert, Observer.DIRECTION_ADD, this.signed.size() - 1); + log("Signed a cert with serial number " + cert.getCertificate().getSerialNumber().getLong()); return cert; } @@ -406,17 +428,22 @@ public class CertificationAuthority { /** * EFFECTS: Add the revocation info to revoked list. This action will be logged. + * Observers will be notified for (RevokedCertificate.class, DIRECTION_ADD, i). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: revoked should have the serial of an issued certificate; its date should be current. * MODIFIES: this */ public void revoke(RevokedCertificate rev) { revoked.add(rev); - log("Certificate " + rev.getSerialNumber().getLong() + " is revoked with reason " + rev.getReason()); + notif(rev, Observer.DIRECTION_ADD, revoked.size() - 1); + log("Certificate " + rev.getSerialNumber().getLong() + " is revoked with reason " + rev.getReason() + + " at " + rev.getRevocationDate().getTimestamp().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); } /** * EFFECTS: Generate and sign the CRL, based on getRevokedCerts(). The CSR will have current time as thisUpdate with * no nextUptime, and it will have issuer same as the CA's subject. + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: The CA cert must be installed first. * MODIFIES: this (This action will be logged) */ @@ -439,10 +466,29 @@ public class CertificationAuthority { /** * EFFECTS: Log the action with the current date and user. + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * MODIFIES: this */ private void log(String message) { - this.logs.add(new AuditLogEntry(user, ZonedDateTime.now(), message)); + final AuditLogEntry i = new AuditLogEntry(user, ZonedDateTime.now(), message); + this.logs.add(i); + notif(i, Observer.DIRECTION_ADD, logs.size() - 1); + } + + /** + * EFFECTS: Register the given observer, so it will be called upon changes. + * MODIFIES: this + */ + public void registerObserver(final Observer observer) { + this.observers.add(observer); + } + + /** + * EFFECTS: Notify the observers. + * REQUIRES: direction must be valid Observer constants, i must be either >= 0 or Observer.INDEX_NOT_IN_LIST. + */ + private void notif(Object o, int direction, int i) { + observers.forEach(e -> e.accept(o, direction, i)); } /** @@ -460,33 +506,43 @@ public class CertificationAuthority { /** * EFFECTS: Install the new template. This action will be logged. + * Observers will be notified for (Template.class, DIRECTION_ADD, i). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: findTemplate(template.getName(), false) == null * MODIFIES: this */ public void addTemplate(Template template) { this.templates.add(template); + notif(template, Observer.DIRECTION_ADD, templates.size() - 1); log("Added a new template: " + template.getName()); } /** - * EFFECTS: Set the given template to enabled / disabled. This action will be logged. + * EFFECTS: Set the given template to enabled / disabled, order will be kept. This action will be logged. + * Observers will be notified for (Template.class, DIRECTION_CHANGE, i). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: the template is valid (findTemplate does not return null) * MODIFIES: this */ public void setTemplateEnable(Template template, boolean enable) { final Template t = findTemplate(template.getName(), false); - templates.remove(t); - templates.add(new Template(t.getName(), enable, t.getSubject(), t.getValidity())); + int i = templates.indexOf(t); + templates.set(i, new Template(t.getName(), enable, t.getSubject(), t.getValidity())); + notif(template, Observer.DIRECTION_CHANGE, i); log("Template " + template.getName() + " has been " + (enable ? "enabled" : "disabled")); } /** * EFFECTS: Remove the given template. This action will be logged. + * Observers will be notified for (Template.class, DIRECTION_REMOVE, i). + * Observers will be notified for (AuditLogEntry.class, DIRECTION_ADD, i). * REQUIRES: the template is valid (findTemplate does not return null) * MODIFIES: this */ public void removeTemplate(Template template) { + int i = templates.indexOf(template); templates.remove(findTemplate(template.getName(), false)); + notif(template, Observer.DIRECTION_REMOVE, i); log("Template " + template.getName() + " is removed"); } @@ -496,40 +552,28 @@ public class CertificationAuthority { return certificate; } - /** - * EFFECT: Get a read-only view of the signed certificates. - */ public List getSigned() { - return List.copyOf(signed); + return signed; } - /** - * EFFECT: Get a read-only view of the revoked certificates. - */ public List getRevoked() { - return List.copyOf(revoked); + return revoked; } public int getSerial() { return serial; } - /** - * EFFECT: Get a read-only view of the templates. - */ public List