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 | |
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>
28 files changed, 1020 insertions, 611 deletions
diff --git a/src/main/model/asn1/ASN1Time.java b/src/main/model/asn1/ASN1Time.java index 8f386f5..94e58b3 100644 --- a/src/main/model/asn1/ASN1Time.java +++ b/src/main/model/asn1/ASN1Time.java @@ -15,7 +15,7 @@ public abstract class ASN1Time extends ASN1Object { /** * The time. */ - private ZonedDateTime timestamp; + private final ZonedDateTime timestamp; /** * EFFECTS: Initialize the time with the specific tag, parentTag, and timestamp. For tag and parentTag, consult diff --git a/src/main/model/asn1/GeneralizedTime.java b/src/main/model/asn1/GeneralizedTime.java index 5482906..c97a51b 100644 --- a/src/main/model/asn1/GeneralizedTime.java +++ b/src/main/model/asn1/GeneralizedTime.java @@ -22,7 +22,7 @@ public class GeneralizedTime extends ASN1Time { /** * Rather stupid impl ... */ - private static final DateTimeFormatter formatterNoSecs = new DateTimeFormatterBuilder() + private static final DateTimeFormatter FORMATTER_NO_SECS = new DateTimeFormatterBuilder() .appendValue(ChronoField.YEAR, 4) .appendValue(ChronoField.MONTH_OF_YEAR, 2) .appendValue(ChronoField.DAY_OF_MONTH, 2) @@ -32,7 +32,7 @@ public class GeneralizedTime extends ASN1Time { .toFormatter() .withZone(ZoneId.of("UTC")); - private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder() + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() .appendValue(ChronoField.YEAR, 4) .appendValue(ChronoField.MONTH_OF_YEAR, 2) .appendValue(ChronoField.DAY_OF_MONTH, 2) @@ -71,7 +71,7 @@ public class GeneralizedTime extends ASN1Time { @Override public ZonedDateTime toDate(String str) throws ParseException { try { - return ZonedDateTime.parse(str, formatter); + return ZonedDateTime.parse(str, FORMATTER); } catch (DateTimeParseException e) { throw new ParseException(e.getMessage()); } @@ -83,8 +83,8 @@ public class GeneralizedTime extends ASN1Time { @Override public String toString() { if (getTimestamp().getSecond() == 0) { - return getTimestamp().format(formatterNoSecs); + return getTimestamp().format(FORMATTER_NO_SECS); } - return getTimestamp().format(formatter); + return getTimestamp().format(FORMATTER); } } diff --git a/src/main/model/asn1/UtcTime.java b/src/main/model/asn1/UtcTime.java index 7fa93d1..54b7a5a 100644 --- a/src/main/model/asn1/UtcTime.java +++ b/src/main/model/asn1/UtcTime.java @@ -23,7 +23,7 @@ public class UtcTime extends ASN1Time { /** * Rather stupid impl ... */ - private static final DateTimeFormatter formatterNoSecs = new DateTimeFormatterBuilder() + private static final DateTimeFormatter FORMATTER_NO_SECS = new DateTimeFormatterBuilder() .appendPattern("yy") .appendValue(ChronoField.MONTH_OF_YEAR, 2) .appendValue(ChronoField.DAY_OF_MONTH, 2) @@ -33,7 +33,7 @@ public class UtcTime extends ASN1Time { .toFormatter() .withZone(ZoneId.of("UTC")); - private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder() + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() .appendPattern("yy") .appendValue(ChronoField.MONTH_OF_YEAR, 2) .appendValue(ChronoField.DAY_OF_MONTH, 2) @@ -72,7 +72,7 @@ public class UtcTime extends ASN1Time { @Override public ZonedDateTime toDate(String str) throws ParseException { try { - return ZonedDateTime.parse(str, formatter); + return ZonedDateTime.parse(str, FORMATTER); } catch (DateTimeParseException e) { throw new ParseException(e.getMessage()); } @@ -84,8 +84,8 @@ public class UtcTime extends ASN1Time { @Override public String toString() { if (getTimestamp().getSecond() == 0) { - return getTimestamp().format(formatterNoSecs); + return getTimestamp().format(FORMATTER_NO_SECS); } - return getTimestamp().format(formatter); + return getTimestamp().format(FORMATTER); } } diff --git a/src/main/model/asn1/exceptions/InvalidCAException.java b/src/main/model/asn1/exceptions/InvalidCAException.java new file mode 100644 index 0000000..e487509 --- /dev/null +++ b/src/main/model/asn1/exceptions/InvalidCAException.java @@ -0,0 +1,10 @@ +package model.asn1.exceptions; + +/** + * Indicate the error that the CA being installed is invalid. + */ +public class InvalidCAException extends Exception { + public InvalidCAException(String message) { + super(message); + } +} diff --git a/src/main/model/ca/CACertificate.java b/src/main/model/ca/CertificationAuthority.java index 1bd53c9..feb557c 100644 --- a/src/main/model/ca/CACertificate.java +++ b/src/main/model/ca/CertificationAuthority.java @@ -1,11 +1,14 @@ package model.ca; import model.asn1.*; +import model.asn1.exceptions.InvalidCAException; import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; import model.csr.*; import model.pki.AlgorithmIdentifier; import model.pki.SubjectPublicKeyInfo; import model.pki.cert.Certificate; +import model.pki.cert.Extension; import model.pki.cert.TbsCertificate; import model.pki.cert.Validity; import model.pki.crl.CertificateList; @@ -18,23 +21,29 @@ import ui.Utils; import java.math.BigInteger; import java.security.*; +import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.stream.Stream; /** - * Holds a CA private key, its certificate, and signed / revoked list. + * Holds a CA private key, its certificate, signed / revoked list, template list, and logs list. */ -public class CACertificate { +public class CertificationAuthority { /** - * The key pair. + * The RSA2048 private key. */ - private KeyPair key; + private RSAPrivateKey key; + + /** + * The public key. + */ + private RSAPublicKey publicKey; /** * The signed certificate. @@ -44,7 +53,7 @@ public class CACertificate { /** * Signed certificates. */ - private List<Certificate> signed; + private final List<Certificate> signed; /** * The next serial number. @@ -54,45 +63,153 @@ public class CACertificate { /** * Revoked certs. */ - private List<RevokedCertificate> revoked; + private final List<RevokedCertificate> revoked; + + /** + * Certificate templates. + */ + private final List<Template> templates; + + /** + * Audit logs. + */ + private final List<AuditLogEntry> logs; + + /** + * Current operator. + */ + private final String user; /** - * EFFECT: Init with a null key and null certificate, empty signed and revoked list, and serial at 1. + * EFFECT: Init with a null key and null certificate, empty signed, revoked template, and log list, serial at 1, and + * user "yuuta". */ - public CACertificate() { + public CertificationAuthority() { this.key = null; + this.publicKey = null; this.certificate = null; this.serial = 1; this.signed = new ArrayList<>(); this.revoked = new ArrayList<>(); + this.templates = new ArrayList<>(); + this.logs = new ArrayList<>(); + this.user = "yuuta"; } /** - * EFFECTS: Generate a new RSA2048 private key. + * EFFECTS: Generate a new RSA2048 private key. This action will be logged. * REQUIRES: getPublicKey() is null (i.e., no private key had been installed) + * MODIFIES: this */ public void generateKey() throws NoSuchAlgorithmException { final KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); gen.initialize(2048); - this.key = gen.generateKeyPair(); + final KeyPair pair = gen.generateKeyPair(); + this.key = (RSAPrivateKey) pair.getPrivate(); + this.publicKey = (RSAPublicKey) pair.getPublic(); + log("Generated CA private key."); } /** - * EFFECT: Install the CA certificate. + * EFFECTS: Load the RSA private and public exponents. This action will be logged. + * 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) * MODIFIES: this - * REQUIRES: - * - The new certificate must have the same algorithm and public key as getPublicKey(), except for testing purpose + */ + public void loadKey(BigInteger n, BigInteger p, BigInteger e) + throws NoSuchAlgorithmException, InvalidKeySpecException { + this.key = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(n, p)); + this.publicKey = + (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(n, e)); + log("Installed CA private key."); + } + + /** + * EFFECTS: Throw {@link InvalidCAException} if the incoming cert is not v3. + */ + private void validateCACertificateVersion(Certificate cert) throws InvalidCAException { + if (cert.getCertificate().getVersion() == null + || cert.getCertificate().getVersion().getLong() != TbsCertificate.VERSION_V3) { + throw new InvalidCAException("The input certificate must be V3"); + } + } + + /** + * EFFECTS: Throw {@link InvalidCAException} if the incoming cert does not have the matching public key. + */ + private void validateCACertificatePublicKey(Certificate cert) throws InvalidCAException { + final SubjectPublicKeyInfo expectedPKInfo = 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 InvalidCAException("The input certificate does not have the corresponding public key"); + } + } + + /** + * EFFECTS: Throw {@link InvalidCAException} if the incoming cert does not have cA = true in its basicConstraints. + */ + private void validateCACertificateBasicConstraints(Certificate cert) throws InvalidCAException, ParseException { + final Extension basicConstraints = cert.getCertificate().getExtension(ObjectIdentifier.OID_BASIC_CONSTRAINTS); + if (basicConstraints == null) { + throw new InvalidCAException("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 InvalidCAException("The certificate does not have a valid basicConstraints extension."); + } + final ASN1Object bool = + ASN1Object.parse(new BytesReader(basicConstraintsValue.encodeValueDER()), false); + if (!((Bool) bool).getValue()) { + throw new InvalidCAException("The certificate does not have a valid basicConstraints extension."); + } + } + + /** + * EFFECTS: Throw {@link InvalidCAException} if the incoming cert does not have valid key usages. + */ + private void validateCACertificateKeyUsage(Certificate cert) throws InvalidCAException, ParseException { + final Extension keyUsage = cert.getCertificate().getExtension(ObjectIdentifier.OID_KEY_USAGE); + if (keyUsage == null) { + throw new InvalidCAException("The certificate does not have a valid keyUsage extension."); + } + final ASN1Object keyUsageValue = + ASN1Object.parse(new BytesReader(keyUsage.getExtnValue().getBytes()), false); + final BitSet bitSet = BitSet.valueOf(Utils.byteToByte(((BitString) keyUsageValue).getVal())); + if (!bitSet.get(7) || !bitSet.get(2) || !bitSet.get(1)) { + throw new InvalidCAException("The certificate does not have a valid keyUsage extension."); + } + } + + /** + * EFFECT: Install the CA certificate. Throws {@link InvalidCAException} if any of the + * following are violated: * - It must be a v3 certificate + * - 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 + * Throws {@link ParseException} if the cert has invalid extension values. + * This action will be logged. + * REQUIRES: * - getCertificate() must be null (i.e., no certificate is installed yet). + * MODIFIES: this */ - public void installCertificate(Certificate certificate) { + public void installCertificate(Certificate certificate) throws InvalidCAException, ParseException { + validateCACertificateVersion(certificate); + validateCACertificatePublicKey(certificate); + validateCACertificateBasicConstraints(certificate); + validateCACertificateKeyUsage(certificate); this.certificate = certificate; + log("CA certificate is installed."); } /** * EFFECTS: Generate a CSR based on public key. It will have subject = CN=JCA. + * REQUIRES: + * - getCertificate() must be null (i.e., no certificate is installed yet). */ private CertificationRequestInfo generateCSR() throws ParseException { return new CertificationRequestInfo(ASN1Object.TAG_SEQUENCE, null, @@ -120,7 +237,7 @@ public class CACertificate { } private Byte[] getPubKeyBitStream() { - final RSAPublicKey pub = (RSAPublicKey) key.getPublic(); + final RSAPublicKey pub = getPublicKey(); final BigInteger exponent = pub.getPublicExponent(); byte[] modules = pub.getModulus().toByteArray(); final Int asn1Exponent = new Int(Int.TAG, null, exponent); @@ -151,14 +268,17 @@ public class CACertificate { /** * EFFECT: Generate CSR and sign it, so the CA can request itself a certificate. * REQUIRES: The CA cert must not be installed. + * MODIFIES: this (This action will be logged) */ public CertificationRequest signCSR() throws ParseException, NoSuchAlgorithmException, SignatureException, InvalidKeyException { final CertificationRequestInfo info = generateCSR(); - return new CertificationRequest(ASN1Object.TAG_SEQUENCE, null, + final CertificationRequest csr = new CertificationRequest(ASN1Object.TAG_SEQUENCE, null, info, getSigningAlgorithm(), new BitString(BitString.TAG, null, 0, signBytes(info.encodeDER()))); + log("Signed CA csr"); + return csr; } /** @@ -185,6 +305,7 @@ public class CACertificate { new BitString(BitString.TAG, null, 0, signBytes(newCert.encodeValueDER()))); this.signed.add(cert); + log("Signed a cert with serial number " + cert.getCertificate().getSerialNumber()); return cert; } @@ -194,7 +315,7 @@ public class CACertificate { private Byte[] signBytes(Byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { final Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initSign(key.getPrivate()); + signature.initSign(key); signature.update(Utils.byteToByte(message)); return Utils.byteToByte(signature.sign()); } @@ -205,6 +326,7 @@ public class CACertificate { * - Issuer will be set to CA#getCertificate()#getSubject() * - The template will be applied (subject, validity, cdp) * - A serial number will be generated + * MODIFIES: this */ private TbsCertificate generateCert(CertificationRequestInfo req, Template template) { final ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC")); @@ -225,57 +347,134 @@ public class CACertificate { } /** - * EFFECTS: Add the revocation info to revoked list. + * EFFECTS: Add the revocation info to revoked list. This action will be logged. * 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()); } /** * 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. * REQUIRES: The CA cert must be installed first. + * MODIFIES: this (This action will be logged) */ public CertificateList signCRL() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - CertificateListContent content = new CertificateListContent(ASN1Object.TAG_SEQUENCE, null, + final CertificateListContent content = new CertificateListContent(ASN1Object.TAG_SEQUENCE, null, certificate.getCertificate().getSubject(), getSigningAlgorithm(), new GeneralizedTime(GeneralizedTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), null, revoked.toArray(new RevokedCertificate[0])); - return new CertificateList(ASN1Object.TAG_SEQUENCE, null, + final CertificateList crl = new CertificateList(ASN1Object.TAG_SEQUENCE, null, content, getSigningAlgorithm(), new BitString(BitString.TAG, null, 0, signBytes(content.encodeValueDER()))); + log("Signed CRL with " + revoked.size() + " revoked certs."); + return crl; + } + + /** + * EFFECTS: Log the action with the current date and user. + * MODIFIES: this + */ + private void log(String message) { + this.logs.add(new AuditLogEntry(user, ZonedDateTime.now(), message)); + } + + /** + * 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); + } + + /** + * EFFECTS: Install the new template. This action will be logged. + * REQUIRES: findTemplate(template.getName(), false) == null + * MODIFIES: this + */ + public void addTemplate(Template template) { + this.templates.add(template); + log("Added a new template: " + template.getName()); + } + + /** + * EFFECTS: Set the given template to enabled / disabled. This action will be logged. + * 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())); + log("Template " + template.getName() + " has been " + (enable ? "enabled" : "disabled")); + } + + /** + * EFFECTS: Remove the given template. This action will be logged. + * REQUIRES: the template is valid (findTemplate does not return null) + * MODIFIES: this + */ + public void removeTemplate(Template template) { + templates.remove(findTemplate(template.getName(), false)); + log("Template " + template.getName() + " is removed"); } + // Getters + public Certificate getCertificate() { return certificate; } + /** + * EFFECT: Get a read-only view of the signed certificates. + */ public List<Certificate> getSigned() { - return signed; + return List.copyOf(signed); } /** - * EFFECTS: Get the public key, or null if no private key is installed. + * EFFECT: Get a read-only view of the revoked certificates. */ - public PublicKey getPublicKey() { - if (key == null) { - return null; - } - return key.getPublic(); - } - public List<RevokedCertificate> getRevoked() { - return revoked; + return List.copyOf(revoked); } public int getSerial() { return serial; } + + /** + * EFFECT: Get a read-only view of the templates. + */ + public List<Template> getTemplates() { + return List.copyOf(templates); + } + + public String getUser() { + return user; + } + + /** + * EFFECT: Get a read-only view of the logs. + */ + public List<AuditLogEntry> getLogs() { + return List.copyOf(logs); + } + + public RSAPublicKey getPublicKey() { + return publicKey; + } } diff --git a/src/main/model/ca/Template.java b/src/main/model/ca/Template.java index af751dc..84e639e 100644 --- a/src/main/model/ca/Template.java +++ b/src/main/model/ca/Template.java @@ -16,22 +16,33 @@ public class Template { /** * Name of the template. */ - private String name; + private final String name; /** * Whether the template is usable or not. */ - private boolean enabled; + private final boolean enabled; /** * Subject of the issued certs. Null -> unspecified */ - private Name subject; + private final Name subject; /** * Length of validity in days since the point of issue. */ - private long validity; + private final long validity; + + /** + * EFFECTS: Init with all given parameters, and commonName will be converted into CN=commonName,C=CA. + * Throws {@link ParseException} if the commonName is invalid. + */ + public Template(String name, + boolean enabled, + String commonName, + long validity) throws ParseException { + this(name, enabled, parseString(commonName), validity); + } /** * EFFECTS: Init with all given parameters. @@ -51,36 +62,24 @@ public class Template { return name; } - public void setName(String name) { - this.name = name; - } - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - public Name getSubject() { return subject; } - public void setSubject(Name subject) { - this.subject = subject; + public long getValidity() { + return validity; } /** - * EFFECTS: Set the subject to CN=commonName,C=CA - * Throws {@link ParseException} if commonName is not a valid PrintableString + * EFFECTS: Convert the given commonName to RDN of CN=commonName,C=CA + * Throws {@link ParseException} if the given commonName is invalid. */ - public void setSubject(String commonName) throws ParseException { - if (commonName == null) { - this.subject = null; - return; - } - setSubject(new Name(ASN1Object.TAG_SEQUENCE, null, new RelativeDistinguishedName[]{ + private static Name parseString(String commonName) throws ParseException { + return new Name(ASN1Object.TAG_SEQUENCE, null, new RelativeDistinguishedName[]{ new RelativeDistinguishedName(ASN1Object.TAG_SET, null, new AttributeTypeAndValue[]{ new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, new ObjectIdentifier(ObjectIdentifier.TAG, null, @@ -90,14 +89,6 @@ public class Template { new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, new ObjectIdentifier(ObjectIdentifier.TAG, null, ObjectIdentifier.OID_C), - new PrintableString(PrintableString.TAG, null, "CA"))})})); - } - - public long getValidity() { - return validity; - } - - public void setValidity(long validity) { - this.validity = validity; + new PrintableString(PrintableString.TAG, null, "CA"))})}); } } 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 + + "-----"; } } diff --git a/src/test/model/TestConstants.java b/src/test/model/TestConstants.java index 04a3aa7..7b2767f 100644 --- a/src/test/model/TestConstants.java +++ b/src/test/model/TestConstants.java @@ -150,18 +150,18 @@ public final class TestConstants { * Signature Value: */ public static final String CERT_L1_ECC_PEM = - "-----BEGIN CERTIFICATE-----\n" + - "MIIBrzCCAVWgAwIBAgIUcPoP+qbX9LSTBV2p0+RCqFJgs/gwCgYIKoZIzj0EAwIw\n" + - "JTEWMBQGA1UEAwwNWXV1dGEgUm9vdCBDQTELMAkGA1UEBhMCQ0EwHhcNMjMwNjIz\n" + - "MDI1MDQ2WhcNNDgwNjIzMDI1MDQ2WjAlMRYwFAYDVQQDDA1ZdXV0YSBSb290IENB\n" + - "MQswCQYDVQQGEwJDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB3ocbzdSHAm\n" + - "cWzdBFs/Xd4UMYs/MYAYKjPlGYYT1udIL5UVOlmN7QnkUxrzYbI1YW5mX1/PCuJl\n" + - "ZT0iKzBxLCSjYzBhMB0GA1UdDgQWBBR4kuBscPWjvgLuRLqnjNrWtUOnkzAfBgNV\n" + - "HSMEGDAWgBR4kuBscPWjvgLuRLqnjNrWtUOnkzAPBgNVHRMBAf8EBTADAQH/MA4G\n" + - "A1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNIADBFAiAJr6S6xkgxMitFmnS/0cIu\n" + - "E7W9Ykii5d2Fe9+Lu4nL9wIhAIwHnAvZ4YzwfY6P5K4SaBwzzKPiq2zdpaXBm0lX\n" + - "qcsz\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIIBrzCCAVWgAwIBAgIUcPoP+qbX9LSTBV2p0+RCqFJgs/gwCgYIKoZIzj0EAwIw\n" + + "JTEWMBQGA1UEAwwNWXV1dGEgUm9vdCBDQTELMAkGA1UEBhMCQ0EwHhcNMjMwNjIz\n" + + "MDI1MDQ2WhcNNDgwNjIzMDI1MDQ2WjAlMRYwFAYDVQQDDA1ZdXV0YSBSb290IENB\n" + + "MQswCQYDVQQGEwJDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB3ocbzdSHAm\n" + + "cWzdBFs/Xd4UMYs/MYAYKjPlGYYT1udIL5UVOlmN7QnkUxrzYbI1YW5mX1/PCuJl\n" + + "ZT0iKzBxLCSjYzBhMB0GA1UdDgQWBBR4kuBscPWjvgLuRLqnjNrWtUOnkzAfBgNV\n" + + "HSMEGDAWgBR4kuBscPWjvgLuRLqnjNrWtUOnkzAPBgNVHRMBAf8EBTADAQH/MA4G\n" + + "A1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNIADBFAiAJr6S6xkgxMitFmnS/0cIu\n" + + "E7W9Ykii5d2Fe9+Lu4nL9wIhAIwHnAvZ4YzwfY6P5K4SaBwzzKPiq2zdpaXBm0lX\n" + + "qcsz\n" + + "-----END CERTIFICATE-----"; /** * Certificate: * Data: @@ -197,31 +197,31 @@ public final class TestConstants { * Signature Value: */ public static final String CERT_L2_RSA_PEM = - "-----BEGIN CERTIFICATE-----\n" + - "MIIEMjCCA9mgAwIBAgIUPhCTneRXjTmH/f9Ce9plWx8hywcwCgYIKoZIzj0EAwQw\n" + - "JTEWMBQGA1UEAwwNWXV1dGEgUm9vdCBDQTELMAkGA1UEBhMCQ0EwHhcNMjMwNjI0\n" + - "MDAxNTIyWhcNMzMwNjIxMDAxNTIyWjBgMRMwEQYKCZImiZPyLGQBGRYDTU9FMRUw\n" + - "EwYKCZImiZPyLGQBGRYFWVVVVEExEjAQBgoJkiaJk/IsZAEZFgJBRDEeMBwGA1UE\n" + - "AwwVWXV1dGEgSG9tZSBJc3N1aW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\n" + - "MIICCgKCAgEA2WTLz4X8B2XN/AiDJNsQQNMiBDjye/TfpbC3dLsUZr4BneH9EX+I\n" + - "lKkaoJaSIXU7iXoN43FeLX5iuoq+aj4f3x+qevWySD7xLLt8gpozN5MKBdRXtaem\n" + - "4js3Nm3YLUbjv115sddHN/3QTQXgBSUGdjyi8woL54IKdKYzB1g2Jn2Et383usMA\n" + - "yHd3gCbwszvE5jpOgBIHxZMgMnmVAQhbQNzoEDMIkwaXmSt4jwX03oigf0KAaD+a\n" + - "XIwQRl15iIDZnG6rRw6+eiIR8c+x1ot1/u5qncwNhRUtLbbX3QfBQ6D/XBSfrqmA\n" + - "zhddM/i2Qt5Iw44CcLSGujFeb9ybU7NLx02EjfQsSAUGQR4VuXyD+//FsLYkh7g3\n" + - "WmdBTWzIhVnYEU9ohTeXaZZNTp9T67czqnntFbaCdOxnwOrcmFt1v0skrHd5mHKe\n" + - "1W3OU6XOjM6vQwcwhPUUGxAXYBcqwQ84fzD26CZz5g8I8HpnpmJ+SNtFIg+SnPOs\n" + - "sslnsoeMZpDPESwORYgayXIWkglop1fYeD4/ictH4me70vOIHF9fWqI8ydHoNxuw\n" + - "uZjZDa0mQgsHTmr40NhDLP/q6MEnS2w/MwHuSd3YbhbjPWFbu0Zo7XreiRkXjRLa\n" + - "R22XkuH+FkEGB3ZxQVIkkWf1znaKQS+ZdPuTzpZph5BPL50gE58k+i0CAwEAAaOB\n" + - "4DCB3TAdBgNVHQ4EFgQUscKngWNmS3IK3f19ICm9a0kJYcAwHwYDVR0jBBgwFoAU\n" + - "eJLgbHD1o74C7kS6p4za1rVDp5MwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B\n" + - "Af8EBAMCAYYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2hvbWUueXV1dGEubW9l\n" + - "L3BraS9yb290Y2EuY3JsMEAGCCsGAQUFBwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0\n" + - "cDovL2hvbWUueXV1dGEubW9lL3BraS9yb290Y2EuY3J0MAoGCCqGSM49BAMEA0cA\n" + - "MEQCIHShp7SwbQ2rQC7l8u4u9rSU6Zl4DRfyor4jiHGAjm0gAiAbOHk6q+3Vm3uq\n" + - "Jj92o1yDl09pFNIryojHMDRBpHl6yQ==\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIIEMjCCA9mgAwIBAgIUPhCTneRXjTmH/f9Ce9plWx8hywcwCgYIKoZIzj0EAwQw\n" + + "JTEWMBQGA1UEAwwNWXV1dGEgUm9vdCBDQTELMAkGA1UEBhMCQ0EwHhcNMjMwNjI0\n" + + "MDAxNTIyWhcNMzMwNjIxMDAxNTIyWjBgMRMwEQYKCZImiZPyLGQBGRYDTU9FMRUw\n" + + "EwYKCZImiZPyLGQBGRYFWVVVVEExEjAQBgoJkiaJk/IsZAEZFgJBRDEeMBwGA1UE\n" + + "AwwVWXV1dGEgSG9tZSBJc3N1aW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\n" + + "MIICCgKCAgEA2WTLz4X8B2XN/AiDJNsQQNMiBDjye/TfpbC3dLsUZr4BneH9EX+I\n" + + "lKkaoJaSIXU7iXoN43FeLX5iuoq+aj4f3x+qevWySD7xLLt8gpozN5MKBdRXtaem\n" + + "4js3Nm3YLUbjv115sddHN/3QTQXgBSUGdjyi8woL54IKdKYzB1g2Jn2Et383usMA\n" + + "yHd3gCbwszvE5jpOgBIHxZMgMnmVAQhbQNzoEDMIkwaXmSt4jwX03oigf0KAaD+a\n" + + "XIwQRl15iIDZnG6rRw6+eiIR8c+x1ot1/u5qncwNhRUtLbbX3QfBQ6D/XBSfrqmA\n" + + "zhddM/i2Qt5Iw44CcLSGujFeb9ybU7NLx02EjfQsSAUGQR4VuXyD+//FsLYkh7g3\n" + + "WmdBTWzIhVnYEU9ohTeXaZZNTp9T67czqnntFbaCdOxnwOrcmFt1v0skrHd5mHKe\n" + + "1W3OU6XOjM6vQwcwhPUUGxAXYBcqwQ84fzD26CZz5g8I8HpnpmJ+SNtFIg+SnPOs\n" + + "sslnsoeMZpDPESwORYgayXIWkglop1fYeD4/ictH4me70vOIHF9fWqI8ydHoNxuw\n" + + "uZjZDa0mQgsHTmr40NhDLP/q6MEnS2w/MwHuSd3YbhbjPWFbu0Zo7XreiRkXjRLa\n" + + "R22XkuH+FkEGB3ZxQVIkkWf1znaKQS+ZdPuTzpZph5BPL50gE58k+i0CAwEAAaOB\n" + + "4DCB3TAdBgNVHQ4EFgQUscKngWNmS3IK3f19ICm9a0kJYcAwHwYDVR0jBBgwFoAU\n" + + "eJLgbHD1o74C7kS6p4za1rVDp5MwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B\n" + + "Af8EBAMCAYYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2hvbWUueXV1dGEubW9l\n" + + "L3BraS9yb290Y2EuY3JsMEAGCCsGAQUFBwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0\n" + + "cDovL2hvbWUueXV1dGEubW9lL3BraS9yb290Y2EuY3J0MAoGCCqGSM49BAMEA0cA\n" + + "MEQCIHShp7SwbQ2rQC7l8u4u9rSU6Zl4DRfyor4jiHGAjm0gAiAbOHk6q+3Vm3uq\n" + + "Jj92o1yDl09pFNIryojHMDRBpHl6yQ==\n" + + "-----END CERTIFICATE-----"; /** * Certificate: @@ -229,7 +229,8 @@ public final class TestConstants { * Version: 1 (0x0) * Serial Number: 3580 (0xdfc) * Signature Algorithm: sha1WithRSAEncryption - * Issuer: C = JP, ST = Tokyo, L = Chuo-ku, O = Frank4DD, OU = WebCert Support, CN = Frank4DD Web CA, emailAddress = support@frank4dd.com + * Issuer: C = JP, ST = Tokyo, L = Chuo-ku, O = Frank4DD, OU = WebCert Support, CN = Frank4DD Web CA, + * emailAddress = support@frank4dd.com * Validity * Not Before: Aug 22 05:27:41 2012 GMT * Not After : Aug 21 05:27:41 2017 GMT @@ -243,24 +244,24 @@ public final class TestConstants { * Signature Value: */ public static final String CERT_V1_PEM = - "-----BEGIN CERTIFICATE-----\n" + - "MIIC2jCCAkMCAg38MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" + - "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" + - "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" + - "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" + - "ODIyMDUyNzQxWhcNMTcwODIxMDUyNzQxWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" + - "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" + - "ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0z9FeMynsC8+u\n" + - "dvX+LciZxnh5uRj4C9S6tNeeAlIGCfQYk0zUcNFCoCkTknNQd/YEiawDLNbxBqut\n" + - "bMDZ1aarys1a0lYmUeVLCIqvzBkPJTSQsCopQQ9V8WuT252zzNzs68dVGNdCJd5J\n" + - "NRQykpwexmnjPPv0mvj7i8XgG379TyW6P+WWV5okeUkXJ9eJS2ouDYdR2SM9BoVW\n" + - "+FgxDu6BmXhozW5EfsnajFp7HL8kQClI0QOc79yuKl3492rH6bzFsFn2lfwWy9ic\n" + - "7cP8EpCTeFp1tFaD+vxBhPZkeTQ1HKx6hQ5zeHIB5ySJJZ7af2W8r4eTGYzbdRW2\n" + - "4DDHCPhZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQMv+BFvGdMVzkQaQ3/+2noVz\n" + - "/uAKbzpEL8xTcxYyP3lkOeh4FoxiSWqy5pGFALdPONoDuYFpLhjJSZaEwuvjI/Tr\n" + - "rGhLV1pRG9frwDFshqD2Vaj4ENBCBh6UpeBop5+285zQ4SI7q4U9oSebUDJiuOx6\n" + - "+tZ9KynmrbJpTSi0+BM=\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIIC2jCCAkMCAg38MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" + + "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" + + "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" + + "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" + + "ODIyMDUyNzQxWhcNMTcwODIxMDUyNzQxWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" + + "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" + + "ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0z9FeMynsC8+u\n" + + "dvX+LciZxnh5uRj4C9S6tNeeAlIGCfQYk0zUcNFCoCkTknNQd/YEiawDLNbxBqut\n" + + "bMDZ1aarys1a0lYmUeVLCIqvzBkPJTSQsCopQQ9V8WuT252zzNzs68dVGNdCJd5J\n" + + "NRQykpwexmnjPPv0mvj7i8XgG379TyW6P+WWV5okeUkXJ9eJS2ouDYdR2SM9BoVW\n" + + "+FgxDu6BmXhozW5EfsnajFp7HL8kQClI0QOc79yuKl3492rH6bzFsFn2lfwWy9ic\n" + + "7cP8EpCTeFp1tFaD+vxBhPZkeTQ1HKx6hQ5zeHIB5ySJJZ7af2W8r4eTGYzbdRW2\n" + + "4DDHCPhZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQMv+BFvGdMVzkQaQ3/+2noVz\n" + + "/uAKbzpEL8xTcxYyP3lkOeh4FoxiSWqy5pGFALdPONoDuYFpLhjJSZaEwuvjI/Tr\n" + + "rGhLV1pRG9frwDFshqD2Vaj4ENBCBh6UpeBop5+285zQ4SI7q4U9oSebUDJiuOx6\n" + + "+tZ9KynmrbJpTSi0+BM=\n" + + "-----END CERTIFICATE-----"; public static final Byte[] CERT_L1_ECC; public static final Byte[] CERT_L2_RSA; diff --git a/src/test/model/asn1/PrintableStringTest.java b/src/test/model/asn1/PrintableStringTest.java index c6eb7b1..08f080b 100644 --- a/src/test/model/asn1/PrintableStringTest.java +++ b/src/test/model/asn1/PrintableStringTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class PrintableStringTest { - private final String ILLEGAL_CHARS_TO_TEST = + private static final String ILLEGAL_CHARS_TO_TEST = "<>\\[]{}@!*&^%$#_|`~"; @Test diff --git a/src/test/model/asn1/UTF8StringTest.java b/src/test/model/asn1/UTF8StringTest.java index 4e879f4..f3cf085 100644 --- a/src/test/model/asn1/UTF8StringTest.java +++ b/src/test/model/asn1/UTF8StringTest.java @@ -18,8 +18,8 @@ public class UTF8StringTest { @Test void testAcceptedString() throws ParseException { final ASN1String string = - new UTF8String(PrintableString.TAG, null, "0123456789abCdExYz '()+,-./:=?*@" + - String.join("", UTF8_CHARS)); + new UTF8String(PrintableString.TAG, null, "0123456789abCdExYz '()+,-./:=?*@" + + String.join("", UTF8_CHARS)); assertEquals("0123456789abCdExYz '()+,-./:=?*@" + String.join("", UTF8_CHARS), string.getString()); } diff --git a/src/test/model/ca/CACertificateTest.java b/src/test/model/ca/CACertificateTest.java deleted file mode 100644 index 4db7bf4..0000000 --- a/src/test/model/ca/CACertificateTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package model.ca; - -import model.TestConstants; -import model.asn1.ASN1Object; -import model.asn1.ObjectIdentifier; -import model.asn1.UtcTime; -import model.asn1.parsing.BytesReader; -import model.csr.CertificationRequest; -import model.csr.CertificationRequestInfo; -import model.pki.cert.Certificate; -import model.pki.crl.Reason; -import model.pki.crl.RevokedCertificate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import ui.Utils; - -import java.nio.charset.StandardCharsets; -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import static org.junit.jupiter.api.Assertions.*; - -public class CACertificateTest { - private static final String CA = "-----BEGIN CERTIFICATE-----\n" + - "MIIC3zCCAoagAwIBAgIUWcL8J0hbxGSffN0fR76j8TlGxJswCgYIKoZIzj0EAwQw\n" + - "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMTMw\n" + - "MTQ3MzFaFw0zMzEwMTAwMTQ3MzFaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + - "hvcNAQEBBQADggEPADCCAQoCggEBAINbCR88MTUsx/poxNzXxN1aWt/DkkFrRA3r\n" + - "dHmLXQLjopULgHIJTshSq2jDe1QEYJ0Nrj9U9YclmxkWO0HvzedmTyl0YzAhPJXj\n" + - "HUK0T9sYSg+eE4WI03yuy7lGBJLUl9VEBR0JEZdy/mT5CRW44ryGGeeBNK3fqQrk\n" + - "5Rm9/wY5M2cKjYmvyp5D8E+HEd+FXNreO+r9pWpKSajPn+B6OwFUUESbRf8iWiF4\n" + - "v6ZLXDOBCEHFZcd2lTVHExuE+V3eDG3evn8HV5SB7FzRDZBV2Jz0Pfiqu2WlH4r8\n" + - "c1804G4WCjQlSX4bPs7994+KjUoFC95r40vexi2O9mVIIEF4LtkCAwEAAaOB4DCB\n" + - "3TAdBgNVHQ4EFgQU+c9PnChWwj4sWHFMN/dikzOS5o8wHwYDVR0jBBgwFoAUba4m\n" + - "yCy2hdnsc6Hhw4m8dvIbit0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" + - "BAMCAYYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2hvbWUueXV1dGEubW9lL3Br\n" + - "aS9yb290Y2EuY3JsMEAGCCsGAQUFBwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0cDov\n" + - "L2hvbWUueXV1dGEubW9lL3BraS9yb290Y2EuY3J0MAoGCCqGSM49BAMEA0cAMEQC\n" + - "IETA9hpUnbrWpLfu2HUWr9UQC273jyg/nt30rJ96PNS+AiAsNzbKVyBpkG41Hf1/\n" + - "+355E7vortNonvf0DDGJZjC7MA==\n" + - "-----END CERTIFICATE-----"; - - private static final String CSR = "-----BEGIN CERTIFICATE REQUEST-----\n" + - "MIIEZjCCAk4CAQAwITELMAkGA1UEBhMCQ0ExEjAQBgNVBAMMCVRlc3QgTGVhZjCC\n" + - "AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYDEoMbxZEuerV5G9IXUXfe\n" + - "UB3u3Yf4b9QI7ewea3Vw04eS/XY4J/KC58OAKc+/3B0Vjghza1+bMalkdFHuIYls\n" + - "/57wbmKIoRSZouma31gHJATWPdDpzcAeZVGRfqfniw3dDfVpIea5gi63gmTFGD7l\n" + - "rmdn5BhQBijWXQY5gD52vGmnalqPBBL+HXgynYiTxmoGI/UNW16V1k8OTnT2F3kt\n" + - "OES+5/mu2r4c7fExmkh64wXYqL2EUvh7xvd4KKIh05Bsl5J2G0Lkl1gh89FJHOVW\n" + - "5+jrMku1wU4KBSZWNvxgcSgfKOI3IAx6iqxflhb7FKK3VTYZ7zJ/cAhaJvv1gJ7N\n" + - "S5AlxsFxMRMgLoFtad9Qk5wH+wX+9Ozf7jNoWZQnLgzfr7CdvjBPmYR/THg0OWFS\n" + - "0bkr20G8lMvtGMbmjN6Ot70KzYCIDjaCV5sX60i76p7rSheibgCslO49cRU7G266\n" + - "HB1GXZNQbT3xBzoaVN9B5uQnL8tUnTn0PsQ4KN2MKIfQt+IO0yesBI7yjXRHSOJX\n" + - "WmbZnrojfYbyAWPBKXnQ4vFcqBdXIXGuI4f67Y8BBuJjV9FOUxcCu++ypP2RtS8h\n" + - "sly2wgtRwPCN7BbLOY9A7qm821DJ3MneHKloGodNvcBvq9VLcwFA9QFX5tgnETV8\n" + - "4oc0VHxaiB7zuNchjINFAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAgEAiRKUSg4m\n" + - "i+qozc3Nx80LfCO9b4+oWp7/bcv0fUADfet7nsobwY8Y6INYMEs6aBNnj2ofFmEd\n" + - "Kup3VHh3vce7Grwkn0MWXKRdCbsLVJ5joWixxxbCDgiRZLYDVlhnU7ZFm3mxmC4l\n" + - "KfKMiHfW833gnemQYRAyamDErKgPV8O9spm0TLj2nLllcA5ugR98kh9TnvnQqRdq\n" + - "dO0ic8C2OECRPV9OVmP13qXiVJRApWYBrw+WJT+sz3LRGfQMIzaTPYWen0dd8+iG\n" + - "HhJot7DNbdLMf6jtWXazrsmUhjjgr5KHMZdOWcbqBCRZkVTkf1HfoRbBTt/wEkBX\n" + - "fJrXVpGbA7H7xXDXKFVUM19q7JJr9M5CfAvCtUGg/UnqfhDnqsFHgQqro21YwNQP\n" + - "/bahU44eNoz8RUiyDEKUW9ginyd0zc3aSAkd98r5u1+tOTmU0KeIr3yc0P+tKxgB\n" + - "bAQaKrXMlLwSHHHutEkJH2KtwKx8w66VtpYtkggfTic1ae6EoVV5LpLHIlZmRMdg\n" + - "CDUatEdRweCdtO0TTR7ik8wMzs6GxAVDfTMaQ41Ks8OBnmLDZQTdRfssm2u6jYut\n" + - "DQxdF5LWe8RVlkEHB2KZJg2fWZ8bjEWr3DkCvnxRlK4Tabo5/mlymjVxTRxxRoGR\n" + - "TXU09TZASjVPzxKIyZbhgNqQvkZl2/hSCE8=\n" + - "-----END CERTIFICATE REQUEST-----"; - - private CACertificate ca; - - @BeforeEach - void setup() { - ca = new CACertificate(); - } - - @Test - void testConstructor() { - assertNull(ca.getPublicKey()); - assertNull(ca.getCertificate()); - assertEquals(1, ca.getSerial()); - assertEquals(0, ca.getSigned().size()); - assertEquals(0, ca.getRevoked().size()); - } - - @Test - void testGenerateKey() throws Throwable { - ca.generateKey(); - assertNotNull(ca.getPublicKey()); - } - - @Test - void testInstallCertificate() throws Throwable { - ca.generateKey(); - ca.installCertificate(new Certificate(new BytesReader(TestConstants.CERT_L2_RSA), false)); - assertNotNull(ca.getCertificate()); - } - - @Test - void testSignCSR() throws Throwable { - ca.generateKey(); - CertificationRequest req = ca.signCSR(); - assertArrayEquals(ObjectIdentifier.OID_SHA256_WITH_RSA_ENCRYPTION, - req.getSignatureAlgorithm().getType().getInts()); - assertEquals("CN=JCA", req.getCertificationRequestInfo().getSubject().toString()); - assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, req.getCertificationRequestInfo().getSubjectPKInfo() - .getAlgorithm().getType().getInts()); - } - - @Test - void testGetCAPublicKeyInfo() throws Throwable { - ca.generateKey(); - ca.installCertificate(new Certificate(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CA.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE")), false)); - assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, - ca.getCAPublicKeyInfo().getAlgorithm().getType().getInts()); - } - - @Test - void testSignCert() throws Throwable { - ca.generateKey(); - ca.installCertificate(new Certificate(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CA.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE")), false)); - CertificationRequestInfo req = new CertificationRequest(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CSR.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE REQUEST")), - false).getCertificationRequestInfo(); - Template template = new Template("123", true, null, 60); - Certificate cert = ca.signCert(req, template); - assertEquals(req.getSubject().toString(), cert.getCertificate().getSubject().toString()); - assertEquals(60, - cert.getCertificate().getValidity().getNotAfter().getTimestamp().getDayOfYear() - - cert.getCertificate().getValidity().getNotBefore().getTimestamp().getDayOfYear()); - assertEquals(1, ca.getSigned().size()); - - template = new Template("123", true, null, 60); - template.setSubject("Test Test"); - cert = ca.signCert(req, template); - assertEquals(60, - cert.getCertificate().getValidity().getNotAfter().getTimestamp().getDayOfYear() - - cert.getCertificate().getValidity().getNotBefore().getTimestamp().getDayOfYear()); - assertEquals(template.getSubject().toString(), cert.getCertificate().getSubject().toString()); - assertEquals(2, ca.getSigned().size()); - } - - @Test - void testRevoke() throws Throwable { - ca.generateKey(); - ca.installCertificate(new Certificate(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CA.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE")), false)); - CertificationRequestInfo req = new CertificationRequest(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CSR.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE REQUEST")), - false).getCertificationRequestInfo(); - Template template = new Template("123", true, null, 60); - Certificate cert = ca.signCert(req, template); - ca.revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, - cert.getCertificate().getSerialNumber(), - new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), - Reason.KEY_COMPROMISE)); - assertEquals(1, ca.getRevoked().size()); - } - - @Test - void testSignCRL() throws Throwable { - ca.generateKey(); - ca.installCertificate(new Certificate(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CA.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE")), false)); - CertificationRequestInfo req = new CertificationRequest(new BytesReader(Utils.parsePEM( - Utils.byteToByte(CSR.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE REQUEST")), - false).getCertificationRequestInfo(); - Template template = new Template("123", true, null, 60); - Certificate cert = ca.signCert(req, template); - ca.revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, - cert.getCertificate().getSerialNumber(), - new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), - Reason.KEY_COMPROMISE)); - assertEquals(1, ca.signCRL().getCrl().getRevokedCertificates().length); - } -} diff --git a/src/test/model/ca/CertificationAuthorityTest.java b/src/test/model/ca/CertificationAuthorityTest.java new file mode 100644 index 0000000..0a9a3eb --- /dev/null +++ b/src/test/model/ca/CertificationAuthorityTest.java @@ -0,0 +1,492 @@ +package model.ca; + +import model.asn1.ASN1Object; +import model.asn1.ObjectIdentifier; +import model.asn1.UtcTime; +import model.asn1.exceptions.InvalidCAException; +import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; +import model.csr.CertificationRequest; +import model.pki.cert.Certificate; +import model.pki.crl.Reason; +import model.pki.crl.RevokedCertificate; +import model.x501.Name; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import ui.Utils; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class CertificationAuthorityTest { + // openssl genrsa -out /dev/stdout 2048 | openssl rsa -text -noout -in /dev/stdin + private static final BigInteger KEY_N = + new BigInteger("00c9fed848b2b17e39b8d1fc27216e" + + "06d0a10030c32c9044e4608cac5a4b" + + "4aa4fa2f6d78f167ae5ac565003976" + + "2696f05e5aa77e734e2bb3becf3b7b" + + "a68eff30f56997c2cb307787a54300" + + "326283aaf917649e5b66aa77459309" + + "d74a70e2e54ce8c242335d7f6e4b8b" + + "a70209ea65ba9ed3bce68595e19299" + + "5576b0f7bc62b8d1d6db4c7ba44a7a" + + "5f5b68321a110869fdb5649ba39683" + + "c291bbecf89ac951325be26dbcf449" + + "82677328375a4ffc3257bc0fe8a261" + + "021cb8b1316723b26e05cd7abd030c" + + "c0eb386604f9ce5eb5dac5047b7002" + + "b9f49e78e390c62705c609a730f5ff" + + "2e59f600b46dd37fff29e013c6b0cd" + + "4b924b365dd0b2a45f833c1fa58180" + + "b7d5", 16); + + private static final BigInteger KEY_P = + new BigInteger("2e432f8270e8ad55e782324bbd008f" + + "cf82fc41eec57b5247f2e3ed0a6e11" + + "8db8de1966b8754c4dae456c587cba" + + "a859ab667c537df1929943737f7659" + + "a689044bc4b0151547c7ac79b95f67" + + "6ac028ad8d81c631fd50bfe9df9c02" + + "a2a239991634fddebf186419dcf402" + + "5f3969a57c692969eb6aff718f0b8e" + + "b315235c12492d87ad047537854adf" + + "8ebcc5f69c930e2f3a51f818225e64" + + "885431049accb474b764aeebae052d" + + "50a094354f2905a600fb0e3314de9c" + + "4f8af3afc16ae2231b09511d60ecba" + + "0bf5f7abaa74a0d2208b52558f2383" + + "85e06672280014a9d545547938606b" + + "947549a0ea9b6e92fa84ac42bdbe91" + + "86c1e428246d635aec1fc0209c0d1b" + + "29", 16); + + + private static final BigInteger KEY_E = new BigInteger("65537", 10); + + private static final String CA_NORMAL = "-----BEGIN CERTIFICATE-----\n" + + "MIICYjCCAgigAwIBAgIUHflZOHj+NxNnCdHe68pxL5ed4GowCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNjMGEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGG\n" + + "MAoGCCqGSM49BAMEA0gAMEUCIEMXMU30a3M5fjhq2M2wsAe5j2d1iuRIn+mXf4BB\n" + + "1uVhAiEA5UynPbqF1zav4/fqPaDB3UyWArzFqi6mjXQUdHOyvXo=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_BASIC_CONSTRAINTS_NO = "-----BEGIN CERTIFICATE-----\n" + + "MIICUDCCAfegAwIBAgIUIBXIjis7QbevVm8ywXyDxxGWW/4wCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNSMFAw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDBANHADBE\n" + + "AiAXYbWzCQ4wjw50avMVtC0H5Z0zWxVJx0f2T1x+2g+5dQIgc8HMxFPYONLCK0hS\n" + + "KNBoht6VUv8UEAabNYI94Oe18J8=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_BASIC_CONSTRAINTS_WRONG = "-----BEGIN CERTIFICATE-----\n" + + "MIICXzCCAgWgAwIBAgIUWMccLZPdSplt4sUkyvjdmm8YvGUwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNgMF4w\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgGGMAoG\n" + + "CCqGSM49BAMEA0gAMEUCIHJD0tNitQbXoDhtfU/0QZzrQOMbGWOjoa/MVDD8sk9f\n" + + "AiEAqJh9gl7CqHhl+g6YpmCt07muQPYLqwg7wA9ieWDMWAo=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_BASIC_CONSTRAINTS_WRONG_1 = "-----BEGIN CERTIFICATE-----\n" + + "MIICYjCCAgigAwIBAgIUWMccLZPdSplt4sUkyvjdmm8YvGUwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNjMGEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAQAwDgYDVR0PAQH/BAQDAgGG\n" + + "MAoGCCqGSM49BAMEA0gAMEUCIHJD0tNitQbXoDhtfU/0QZzrQOMbGWOjoa/MVDD8\n" + + "sk9fAiEAqJh9gl7CqHhl+g6YpmCt07muQPYLqwg7wA9ieWDMWAo=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_KEY_USAGE_NO = "-----BEGIN CERTIFICATE-----\n" + + "MIICUTCCAfigAwIBAgIUc6s/9WDXTrYkTZAnQIwKVCiugsowCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNTMFEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwQDRwAw\n" + + "RAIgCYoK8MHJ4VDL5nlfn9QBu+TDNJ3pQye1P5fAVxlSjXoCIHBXQ7/GiV5boVZh\n" + + "+I4BMH1A7iD3T4w5Bac9JWLqjOiw\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_KEY_USAGE_WRONG_1 = "-----BEGIN CERTIFICATE-----\n" + + "MIICYjCCAgigAwIBAgIUFBXd38tfIIwA3nQiNLEUellWeVwwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNjMGEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG\n" + + "MAoGCCqGSM49BAMEA0gAMEUCIQCHBeDQIUvYlFv8f/YjbDb5nNyUiH/w66VbSqDd\n" + + "OnQlyQIgVvvmCin5sMXFN0t90ZxlT9QtX6nXWq5nPuLNiNb/Ka8=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_KEY_USAGE_WRONG_2 = "-----BEGIN CERTIFICATE-----\n" + + "MIICYjCCAgigAwIBAgIUFlQfTUXmYWgVMi8IZxP2LMztBoEwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNjMGEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKE\n" + + "MAoGCCqGSM49BAMEA0gAMEUCIQC0wekpTe83hkgxSDs8odWWZX51bz3r0xQE7NNk\n" + + "PnWyZQIgDzKe0mPQg0bMlmA7JfuWKJEPNej9b94qb9dYb/u4484=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_KEY_USAGE_WRONG_3 = "-----BEGIN CERTIFICATE-----\n" + + "MIICYjCCAgigAwIBAgIUEB4O3DWS3LG9+p7Dm7ERCDIsknAwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAAaNjMGEw\n" + + "HQYDVR0OBBYEFKLntJQ7phJGu7A9dfIovZy6rV6SMB8GA1UdIwQYMBaAFPMn0b1s\n" + + "t26LXxJKvjFSvn8X1IetMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGC\n" + + "MAoGCCqGSM49BAMEA0gAMEUCIQCLEeyvD7Ap2ZJuNn5ElkdgT7TugBuyZu1EBAhZ\n" + + "emddIAIgK+h7W/SRn7IC3fgfLui9qp/Sdsdpv/x6gm/Ly6e2c8Y=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_V1 = "-----BEGIN CERTIFICATE-----\n" + + "MIIB+DCCAZ4CFDLEYIlbLYpdN7KCcbYs8vWup6h5MAoGCCqGSM49BAMEMCQxFTAT\n" + + "BgNVBAMMDFRlc3QgUm9vdCBDQTELMAkGA1UEBhMCQ0EwHhcNMjMxMDI0MDUzMDAy\n" + + "WhcNMzMxMDIxMDUzMDAyWjAOMQwwCgYDVQQDEwNKQ0EwggEiMA0GCSqGSIb3DQEB\n" + + "AQUAA4IBDwAwggEKAoIBAQDJ/thIsrF+ObjR/CchbgbQoQAwwyyQRORgjKxaS0qk\n" + + "+i9tePFnrlrFZQA5diaW8F5ap35zTiuzvs87e6aO/zD1aZfCyzB3h6VDADJig6r5\n" + + "F2SeW2aqd0WTCddKcOLlTOjCQjNdf25Li6cCCeplup7TvOaFleGSmVV2sPe8YrjR\n" + + "1ttMe6RKel9baDIaEQhp/bVkm6OWg8KRu+z4mslRMlvibbz0SYJncyg3Wk/8Mle8\n" + + "D+iiYQIcuLExZyOybgXNer0DDMDrOGYE+c5etdrFBHtwArn0nnjjkMYnBcYJpzD1\n" + + "/y5Z9gC0bdN//yngE8awzUuSSzZd0LKkX4M8H6WBgLfVAgMBAAEwCgYIKoZIzj0E\n" + + "AwQDSAAwRQIhAL7p/tcs0GF85oepkgvbFWgadeDtL070sUZNaj+vKBsVAiBv/2Zp\n" + + "fd64OJmI+xefF51qrFW212UjZA7H8JY91LHKaw==\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_WRONG_KEY = "-----BEGIN CERTIFICATE-----\n" + + "MIIC3zCCAoagAwIBAgIUWcL8J0hbxGSffN0fR76j8TlGxJswCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMTMw\n" + + "MTQ3MzFaFw0zMzEwMTAwMTQ3MzFaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAINbCR88MTUsx/poxNzXxN1aWt/DkkFrRA3r\n" + + "dHmLXQLjopULgHIJTshSq2jDe1QEYJ0Nrj9U9YclmxkWO0HvzedmTyl0YzAhPJXj\n" + + "HUK0T9sYSg+eE4WI03yuy7lGBJLUl9VEBR0JEZdy/mT5CRW44ryGGeeBNK3fqQrk\n" + + "5Rm9/wY5M2cKjYmvyp5D8E+HEd+FXNreO+r9pWpKSajPn+B6OwFUUESbRf8iWiF4\n" + + "v6ZLXDOBCEHFZcd2lTVHExuE+V3eDG3evn8HV5SB7FzRDZBV2Jz0Pfiqu2WlH4r8\n" + + "c1804G4WCjQlSX4bPs7994+KjUoFC95r40vexi2O9mVIIEF4LtkCAwEAAaOB4DCB\n" + + "3TAdBgNVHQ4EFgQU+c9PnChWwj4sWHFMN/dikzOS5o8wHwYDVR0jBBgwFoAUba4m\n" + + "yCy2hdnsc6Hhw4m8dvIbit0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" + + "BAMCAYYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2hvbWUueXV1dGEubW9lL3Br\n" + + "aS9yb290Y2EuY3JsMEAGCCsGAQUFBwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0cDov\n" + + "L2hvbWUueXV1dGEubW9lL3BraS9yb290Y2EuY3J0MAoGCCqGSM49BAMEA0cAMEQC\n" + + "IETA9hpUnbrWpLfu2HUWr9UQC273jyg/nt30rJ96PNS+AiAsNzbKVyBpkG41Hf1/\n" + + "+355E7vortNonvf0DDGJZjC7MA==\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_WRONG_KEY_ECC = "-----BEGIN CERTIFICATE-----\n" + + "MIIBrjCCAVOgAwIBAgIUUd7mZGEF1iT9+R3K7y+M/k6P2L4wCgYIKoZIzj0EAwIw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMTMy\n" + + "MTI3MzdaFw00ODEwMTMyMTI3MzdaMCQxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTEL\n" + + "MAkGA1UEBhMCQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjgXFX7Ul6Zmeg\n" + + "cTDw460HUpBTaxKFrRFJ66BbYvmAu+yoBB8Yc+pDV+qo6M5EdjwmAEeeI/BGlZ4m\n" + + "RfO+rfQTo2MwYTAdBgNVHQ4EFgQU8yfRvWy3botfEkq+MVK+fxfUh60wHwYDVR0j\n" + + "BBgwFoAU8yfRvWy3botfEkq+MVK+fxfUh60wDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" + + "HQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSQAwRgIhALFKqvFhzHPNRBWwjKho8QUL\n" + + "ztHOtTXmnM9BN6eS5edlAiEAm8PTq0e9a9pqwovLXtAFXlb6iuvj85pACvBdvqqJ\n" + + "qeQ=\n" + + "-----END CERTIFICATE-----"; + + private static final String CA_V2 = "-----BEGIN CERTIFICATE-----\n" + + "MIIB/TCCAaOgAwIBAQIUMsRgiVstil03soJxtizy9a6nqHkwCgYIKoZIzj0EAwQw\n" + + "JDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJDQTAeFw0yMzEwMjQw\n" + + "NTMwMDJaFw0zMzEwMjEwNTMwMDJaMA4xDDAKBgNVBAMTA0pDQTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMn+2EiysX45uNH8JyFuBtChADDDLJBE5GCM\n" + + "rFpLSqT6L2148WeuWsVlADl2JpbwXlqnfnNOK7O+zzt7po7/MPVpl8LLMHeHpUMA\n" + + "MmKDqvkXZJ5bZqp3RZMJ10pw4uVM6MJCM11/bkuLpwIJ6mW6ntO85oWV4ZKZVXaw\n" + + "97xiuNHW20x7pEp6X1toMhoRCGn9tWSbo5aDwpG77PiayVEyW+JtvPRJgmdzKDda\n" + + "T/wyV7wP6KJhAhy4sTFnI7JuBc16vQMMwOs4ZgT5zl612sUEe3ACufSeeOOQxicF\n" + + "xgmnMPX/Lln2ALRt03//KeATxrDNS5JLNl3QsqRfgzwfpYGAt9UCAwEAATAKBggq\n" + + "hkjOPQQDBANIADBFAiEAvun+1yzQYXzmh6mSC9sVaBp14O0vTvSxRk1qP68oGxUC\n" + + "IG//Zml93rg4mYj7F58XnWqsVbbXZSNkDsfwlj3Uscpr\n" + + "-----END CERTIFICATE-----"; + + private static final String CSR = "-----BEGIN CERTIFICATE REQUEST-----\n" + + "MIIEZjCCAk4CAQAwITELMAkGA1UEBhMCQ0ExEjAQBgNVBAMMCVRlc3QgTGVhZjCC\n" + + "AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYDEoMbxZEuerV5G9IXUXfe\n" + + "UB3u3Yf4b9QI7ewea3Vw04eS/XY4J/KC58OAKc+/3B0Vjghza1+bMalkdFHuIYls\n" + + "/57wbmKIoRSZouma31gHJATWPdDpzcAeZVGRfqfniw3dDfVpIea5gi63gmTFGD7l\n" + + "rmdn5BhQBijWXQY5gD52vGmnalqPBBL+HXgynYiTxmoGI/UNW16V1k8OTnT2F3kt\n" + + "OES+5/mu2r4c7fExmkh64wXYqL2EUvh7xvd4KKIh05Bsl5J2G0Lkl1gh89FJHOVW\n" + + "5+jrMku1wU4KBSZWNvxgcSgfKOI3IAx6iqxflhb7FKK3VTYZ7zJ/cAhaJvv1gJ7N\n" + + "S5AlxsFxMRMgLoFtad9Qk5wH+wX+9Ozf7jNoWZQnLgzfr7CdvjBPmYR/THg0OWFS\n" + + "0bkr20G8lMvtGMbmjN6Ot70KzYCIDjaCV5sX60i76p7rSheibgCslO49cRU7G266\n" + + "HB1GXZNQbT3xBzoaVN9B5uQnL8tUnTn0PsQ4KN2MKIfQt+IO0yesBI7yjXRHSOJX\n" + + "WmbZnrojfYbyAWPBKXnQ4vFcqBdXIXGuI4f67Y8BBuJjV9FOUxcCu++ypP2RtS8h\n" + + "sly2wgtRwPCN7BbLOY9A7qm821DJ3MneHKloGodNvcBvq9VLcwFA9QFX5tgnETV8\n" + + "4oc0VHxaiB7zuNchjINFAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAgEAiRKUSg4m\n" + + "i+qozc3Nx80LfCO9b4+oWp7/bcv0fUADfet7nsobwY8Y6INYMEs6aBNnj2ofFmEd\n" + + "Kup3VHh3vce7Grwkn0MWXKRdCbsLVJ5joWixxxbCDgiRZLYDVlhnU7ZFm3mxmC4l\n" + + "KfKMiHfW833gnemQYRAyamDErKgPV8O9spm0TLj2nLllcA5ugR98kh9TnvnQqRdq\n" + + "dO0ic8C2OECRPV9OVmP13qXiVJRApWYBrw+WJT+sz3LRGfQMIzaTPYWen0dd8+iG\n" + + "HhJot7DNbdLMf6jtWXazrsmUhjjgr5KHMZdOWcbqBCRZkVTkf1HfoRbBTt/wEkBX\n" + + "fJrXVpGbA7H7xXDXKFVUM19q7JJr9M5CfAvCtUGg/UnqfhDnqsFHgQqro21YwNQP\n" + + "/bahU44eNoz8RUiyDEKUW9ginyd0zc3aSAkd98r5u1+tOTmU0KeIr3yc0P+tKxgB\n" + + "bAQaKrXMlLwSHHHutEkJH2KtwKx8w66VtpYtkggfTic1ae6EoVV5LpLHIlZmRMdg\n" + + "CDUatEdRweCdtO0TTR7ik8wMzs6GxAVDfTMaQ41Ks8OBnmLDZQTdRfssm2u6jYut\n" + + "DQxdF5LWe8RVlkEHB2KZJg2fWZ8bjEWr3DkCvnxRlK4Tabo5/mlymjVxTRxxRoGR\n" + + "TXU09TZASjVPzxKIyZbhgNqQvkZl2/hSCE8=\n" + + "-----END CERTIFICATE REQUEST-----"; + + private Certificate crtNormal; + private Certificate crtBasicConstraintsNo; + private Certificate crtBasicConstraintsWrong; + private Certificate crtBasicConstraintsWrong1; + private Certificate crtKeyUsageNo; + private Certificate crtKeyUsageWrong1; + private Certificate crtKeyUsageWrong2; + private Certificate crtKeyUsageWrong3; + private Certificate crtWrongKey; + private Certificate crtWrongKeyECC; + private Certificate crtV1; + private Certificate crtV2; + private Template template; + + private CertificationRequest csr; + + private CertificationAuthority ca; + + private CertificationAuthority caWithPrivateKey; + + private static Certificate getCert(String pem) throws ParseException { + return new Certificate(new BytesReader(Utils.parsePEM(Utils.byteToByte(pem.getBytes(StandardCharsets.UTF_8)), + "CERTIFICATE")), false); + } + + @BeforeEach + void setupCerts() throws Throwable { + crtNormal = getCert(CA_NORMAL); + crtBasicConstraintsNo = getCert(CA_BASIC_CONSTRAINTS_NO); + crtBasicConstraintsWrong = getCert(CA_BASIC_CONSTRAINTS_WRONG); + crtBasicConstraintsWrong1 = getCert(CA_BASIC_CONSTRAINTS_WRONG_1); + crtKeyUsageNo = getCert(CA_KEY_USAGE_NO); + crtKeyUsageWrong1 = getCert(CA_KEY_USAGE_WRONG_1); + crtKeyUsageWrong2 = getCert(CA_KEY_USAGE_WRONG_2); + crtKeyUsageWrong3 = getCert(CA_KEY_USAGE_WRONG_3); + crtWrongKey = getCert(CA_WRONG_KEY); + crtWrongKeyECC = getCert(CA_WRONG_KEY_ECC); + crtV1 = getCert(CA_V1); + crtV2 = getCert(CA_V2); + csr = new CertificationRequest(new BytesReader(Utils.parsePEM( + Utils.byteToByte(CSR.getBytes(StandardCharsets.UTF_8)), "CERTIFICATE REQUEST")), + false); + template = new Template("123", true, (Name) null, 60); + } + + @BeforeEach + void setup() throws Throwable { + ca = new CertificationAuthority(); + caWithPrivateKey = new CertificationAuthority(); + caWithPrivateKey.loadKey(KEY_N, KEY_P, KEY_E); + } + + @Test + void testConstructor() { + assertNull(ca.getPublicKey()); + assertNull(ca.getCertificate()); + assertEquals(1, ca.getSerial()); + assertEquals(0, ca.getSigned().size()); + assertEquals(0, ca.getRevoked().size()); + assertEquals(0, ca.getTemplates().size()); + assertEquals(0, ca.getLogs().size()); + assertEquals("yuuta", ca.getUser()); + } + + @Test + void testGenerateKey() throws Throwable { + int logCount = ca.getLogs().size(); + ca.generateKey(); + assertNotNull(ca.getPublicKey()); + assertEquals(logCount + 1, ca.getLogs().size()); + } + + @Test + void testInstallCertificate() throws Throwable { + int logCount = caWithPrivateKey.getLogs().size(); + caWithPrivateKey.installCertificate(crtNormal); + assertNotNull(caWithPrivateKey.getCertificate()); + assertEquals(logCount + 1, caWithPrivateKey.getLogs().size()); + } + + @Test + void testInstallCertificateFailed() { + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtBasicConstraintsNo)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtBasicConstraintsWrong)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtBasicConstraintsWrong1)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtKeyUsageNo)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtKeyUsageWrong1)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtKeyUsageWrong2)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtKeyUsageWrong3)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtWrongKey)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtWrongKeyECC)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtV1)); + assertThrows(InvalidCAException.class, () -> + caWithPrivateKey.installCertificate(crtV2)); + } + + @Test + void testSignCSR() throws Throwable { + caWithPrivateKey.installCertificate(crtNormal); + int logCount = caWithPrivateKey.getLogs().size(); + CertificationRequest req = caWithPrivateKey.signCSR(); + assertArrayEquals(ObjectIdentifier.OID_SHA256_WITH_RSA_ENCRYPTION, + req.getSignatureAlgorithm().getType().getInts()); + assertEquals("CN=JCA", req.getCertificationRequestInfo().getSubject().toString()); + assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, req.getCertificationRequestInfo().getSubjectPKInfo() + .getAlgorithm().getType().getInts()); + assertEquals(logCount + 1, caWithPrivateKey.getLogs().size()); + } + + @Test + void testGetCAPublicKeyInfo() throws Throwable { + caWithPrivateKey.installCertificate(crtNormal); + assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, + caWithPrivateKey.getCAPublicKeyInfo().getAlgorithm().getType().getInts()); + } + + @Test + void testSignCert() throws Throwable { + caWithPrivateKey.installCertificate(crtNormal); + + int logCount = caWithPrivateKey.getLogs().size(); + + Certificate cert = caWithPrivateKey.signCert(csr.getCertificationRequestInfo(), + new Template(template.getName(), true, (Name) null, template.getValidity())); + assertEquals(csr.getCertificationRequestInfo().getSubject().toString(), + cert.getCertificate().getSubject().toString()); + assertEquals(60, + cert.getCertificate().getValidity().getNotAfter().getTimestamp().getDayOfYear() + - cert.getCertificate().getValidity().getNotBefore().getTimestamp().getDayOfYear()); + assertEquals(1, caWithPrivateKey.getSigned().size()); + assertEquals(logCount + 1, caWithPrivateKey.getLogs().size()); + + Template tmp = new Template(template.getName(), true, "ABCC", template.getValidity()); + cert = caWithPrivateKey.signCert(csr.getCertificationRequestInfo(), tmp); + assertEquals(60, + cert.getCertificate().getValidity().getNotAfter().getTimestamp().getDayOfYear() + - cert.getCertificate().getValidity().getNotBefore().getTimestamp().getDayOfYear()); + assertEquals(tmp.getSubject().toString(), cert.getCertificate().getSubject().toString()); + assertEquals(2, caWithPrivateKey.getSigned().size()); + assertEquals(logCount + 2, caWithPrivateKey.getLogs().size()); + } + + @Test + void testRevoke() throws Throwable { + caWithPrivateKey.installCertificate(crtNormal); + + int logCount = caWithPrivateKey.getLogs().size(); + + Certificate cert = caWithPrivateKey.signCert(csr.getCertificationRequestInfo(), template); + assertEquals(++logCount, caWithPrivateKey.getLogs().size()); + caWithPrivateKey.revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, + cert.getCertificate().getSerialNumber(), + new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), + Reason.KEY_COMPROMISE)); + assertEquals(++logCount, caWithPrivateKey.getLogs().size()); + assertEquals(1, caWithPrivateKey.getRevoked().size()); + } + + @Test + void testSignCRL() throws Throwable { + caWithPrivateKey.installCertificate(crtNormal); + + int logCount = caWithPrivateKey.getLogs().size(); + + Certificate cert = caWithPrivateKey.signCert(csr.getCertificationRequestInfo(), template); + assertEquals(++logCount, caWithPrivateKey.getLogs().size()); + + caWithPrivateKey.revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, + cert.getCertificate().getSerialNumber(), + new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), + Reason.KEY_COMPROMISE)); + assertEquals(++logCount, caWithPrivateKey.getLogs().size()); + assertEquals(1, caWithPrivateKey.signCRL().getCrl().getRevokedCertificates().length); + assertEquals(++logCount, caWithPrivateKey.getLogs().size()); + } + + @Test + void testTemplateOperations() { + int logCount = ca.getLogs().size(); + assertNull(ca.findTemplate(template.getName(), false)); + + ca.addTemplate(template); + assertEquals(++logCount, ca.getLogs().size()); + assertNotNull(ca.findTemplate(template.getName(), false)); + + ca.setTemplateEnable(template, false); + assertEquals(++logCount, ca.getLogs().size()); + assertNull(ca.findTemplate(template.getName(), true)); + + ca.setTemplateEnable(template, true); + assertEquals(++logCount, ca.getLogs().size()); + assertNotNull(ca.findTemplate(template.getName(), true)); + + ca.removeTemplate(template); + assertEquals(++logCount, ca.getLogs().size()); + assertNull(ca.findTemplate(template.getName(), false)); + } +} diff --git a/src/test/model/ca/TemplateTest.java b/src/test/model/ca/TemplateTest.java index 4014599..1926078 100644 --- a/src/test/model/ca/TemplateTest.java +++ b/src/test/model/ca/TemplateTest.java @@ -13,48 +13,35 @@ import static org.junit.jupiter.api.Assertions.*; public class TemplateTest { @Test - void testConstructor() { - Template template = new Template("123", true, null, 123); + void testConstructor() throws ParseException { + Template template = new Template("123", true, (Name) null, 123); assertEquals("123", template.getName()); assertTrue(template.isEnabled()); assertNull(template.getSubject()); assertEquals(123, template.getValidity()); - } - @Test - void testSetSubject() throws ParseException { - Template template = new Template("123", true, null, 123); - template.setSubject("123"); - assertEquals("CN=123,C=CA", template.getSubject().toString()); - template.setSubject((String) null); - assertNull(template.getSubject()); - assertThrows(ParseException.class, () -> template.setSubject("*")); + template = new Template("123", true, + new Name(ASN1Object.TAG_SEQUENCE, null, new RelativeDistinguishedName[]{ + new RelativeDistinguishedName(ASN1Object.TAG_SET, null, new AttributeTypeAndValue[]{ + new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, + new ObjectIdentifier(ObjectIdentifier.TAG, null, + ObjectIdentifier.OID_CN), + new PrintableString(PrintableString.TAG, null, "Test"))}), + new RelativeDistinguishedName(ASN1Object.TAG_SET, null, new AttributeTypeAndValue[]{ + new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, + new ObjectIdentifier(ObjectIdentifier.TAG, null, + ObjectIdentifier.OID_C), + new PrintableString(PrintableString.TAG, null, "CA"))})}), + 123); + assertEquals("CN=Test,C=CA", template.getSubject().toString()); } @Test - void testSetters() throws ParseException { - Template template = new Template("123", true, null, 123); - - template.setName("114514"); - assertEquals("114514", template.getName()); - - template.setSubject(new Name(ASN1Object.TAG_SEQUENCE, null, new RelativeDistinguishedName[]{ - new RelativeDistinguishedName(ASN1Object.TAG_SET, null, new AttributeTypeAndValue[]{ - new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, - new ObjectIdentifier(ObjectIdentifier.TAG, null, - ObjectIdentifier.OID_CN), - new PrintableString(PrintableString.TAG, null, "Test"))}), - new RelativeDistinguishedName(ASN1Object.TAG_SET, null, new AttributeTypeAndValue[]{ - new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, - new ObjectIdentifier(ObjectIdentifier.TAG, null, - ObjectIdentifier.OID_C), - new PrintableString(PrintableString.TAG, null, "CA"))})})); - assertEquals("CN=Test,C=CA", template.getSubject().toString()); - - template.setEnabled(false); - assertFalse(template.isEnabled()); + void testSetSubject() throws ParseException { + Template template = new Template("123", true, "ABC", 123); + assertEquals("CN=ABC,C=CA", template.getSubject().toString()); - template.setValidity(233); - assertEquals(233, template.getValidity()); + assertThrows(ParseException.class, () -> new Template("123", true, "*", + 123)); } } diff --git a/src/test/model/csr/AttributesTest.java b/src/test/model/csr/AttributesTest.java index eebdbfb..5c40a65 100644 --- a/src/test/model/csr/AttributesTest.java +++ b/src/test/model/csr/AttributesTest.java @@ -59,7 +59,7 @@ public class AttributesTest { Byte[] a2 = TestConstants.CSR_ATTR_2.encodeDER(); Byte[] a1 = TestConstants.CSR_ATTR_1.encodeDER(); assertArrayEquals( - Stream.of(Arrays.asList(new Byte[]{0x31, (byte) (a2.length + a1.length)}), + Stream.of(Arrays.asList((byte) 0x31, (byte) (a2.length + a1.length)), Arrays.asList(a2), Arrays.asList(a1)) .flatMap(Collection::stream) diff --git a/src/test/model/pki/cert/ExtensionsTest.java b/src/test/model/pki/cert/ExtensionsTest.java index e50b3e6..e4d9e0b 100644 --- a/src/test/model/pki/cert/ExtensionsTest.java +++ b/src/test/model/pki/cert/ExtensionsTest.java @@ -35,14 +35,16 @@ public class ExtensionsTest { assertEquals(2, extensions.getExtensions().length); assertArrayEquals(ObjectIdentifier.OID_BASIC_CONSTRAINTS, extensions.getExtensions()[0].getExtnId().getInts()); - assertArrayEquals(ObjectIdentifier.OID_SUBJECT_KEY_IDENTIFIER, extensions.getExtensions()[1].getExtnId().getInts()); + assertArrayEquals(ObjectIdentifier.OID_SUBJECT_KEY_IDENTIFIER, + extensions.getExtensions()[1].getExtnId().getInts()); } @Test void testParse() throws ParseException { final Extensions parsed = new Extensions(new BytesReader( - Stream.of(Arrays.asList(new Byte[]{0x30, - (byte) (ExtensionTest.EXT_KEY_USAGE.length + ExtensionTest.EXT_SUBJECT_KEY_ID.length)}), + Stream.of(Arrays.asList((byte) 0x30, + (byte) (ExtensionTest.EXT_KEY_USAGE.length + + ExtensionTest.EXT_SUBJECT_KEY_ID.length)), Arrays.asList(ExtensionTest.EXT_KEY_USAGE), Arrays.asList(ExtensionTest.EXT_SUBJECT_KEY_ID)) .flatMap(Collection::stream) @@ -58,8 +60,9 @@ public class ExtensionsTest { }); assertThrows(ParseException.class, () -> { Byte[] bytes = - Stream.of(Arrays.asList(new Byte[]{0x30, - (byte) (ExtensionTest.EXT_KEY_USAGE.length + ExtensionTest.EXT_SUBJECT_KEY_ID.length)}), + Stream.of(Arrays.asList((byte) 0x30, + (byte) (ExtensionTest.EXT_KEY_USAGE.length + + ExtensionTest.EXT_SUBJECT_KEY_ID.length)), Arrays.asList(ExtensionTest.EXT_KEY_USAGE), Arrays.asList(ExtensionTest.EXT_SUBJECT_KEY_ID)) .flatMap(Collection::stream) @@ -70,8 +73,9 @@ public class ExtensionsTest { }); assertThrows(ParseException.class, () -> { Byte[] bytes = - Stream.of(Arrays.asList(new Byte[]{0x30, - (byte) (ExtensionTest.EXT_KEY_USAGE.length + ExtensionTest.EXT_SUBJECT_KEY_ID.length)}), + Stream.of(Arrays.asList((byte) 0x30, + (byte) (ExtensionTest.EXT_KEY_USAGE.length + + ExtensionTest.EXT_SUBJECT_KEY_ID.length)), Arrays.asList(ExtensionTest.EXT_KEY_USAGE), Arrays.asList(ExtensionTest.EXT_SUBJECT_KEY_ID)) .flatMap(Collection::stream) @@ -85,16 +89,17 @@ public class ExtensionsTest { @Test void testEncode() { assertArrayEquals( - Stream.of(Arrays.asList(new Byte[]{0x30, - (byte) (ExtensionTest.EXT_KEY_USAGE.length + - ExtensionTest.EXT_SUBJECT_KEY_ID.length)}), + Stream.of(Arrays.asList((byte) 0x30, + (byte) (ExtensionTest.EXT_KEY_USAGE.length + + ExtensionTest.EXT_SUBJECT_KEY_ID.length)), Arrays.asList(ExtensionTest.EXT_SUBJECT_KEY_ID), Arrays.asList(ExtensionTest.EXT_KEY_USAGE)) .flatMap(Collection::stream) .toArray(Byte[]::new), new Extensions(ASN1Object.TAG_SEQUENCE, null, new Extension[]{ new Extension(ASN1Object.TAG_SEQUENCE, null, - new ObjectIdentifier(ObjectIdentifier.TAG, null, ObjectIdentifier.OID_SUBJECT_KEY_IDENTIFIER), + new ObjectIdentifier(ObjectIdentifier.TAG, null, + ObjectIdentifier.OID_SUBJECT_KEY_IDENTIFIER), null, new OctetString(OctetString.TAG, null, new Byte[]{ 0x04, 0x14, -79, -62, -89, -127, 0x63, 0x66, @@ -102,7 +107,8 @@ public class ExtensionsTest { -67, 0x6B, 0x49, 0x09, 0x61, -64 })), new Extension(ASN1Object.TAG_SEQUENCE, null, - new ObjectIdentifier(ObjectIdentifier.TAG, null, ObjectIdentifier.OID_KEY_USAGE), + new ObjectIdentifier(ObjectIdentifier.TAG, null, + ObjectIdentifier.OID_KEY_USAGE), new Bool(Bool.TAG, null, true), new OctetString(OctetString.TAG, null, new Byte[]{ 0x03, 0x02, 0x01, -122 diff --git a/src/test/model/pki/cert/TbsCertificateTest.java b/src/test/model/pki/cert/TbsCertificateTest.java index ee2a9c7..e778350 100644 --- a/src/test/model/pki/cert/TbsCertificateTest.java +++ b/src/test/model/pki/cert/TbsCertificateTest.java @@ -21,7 +21,8 @@ public class TbsCertificateTest { void testConstructor() { assertEquals(TbsCertificate.VERSION_V3, TestConstants.CERT_GENERATED.getVersion().getLong()); assertEquals(100, TestConstants.CERT_GENERATED.getSerialNumber().getLong()); - assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, TestConstants.CERT_GENERATED.getSignature().getType().getInts()); + assertArrayEquals(ObjectIdentifier.OID_RSA_ENCRYPTION, + TestConstants.CERT_GENERATED.getSignature().getType().getInts()); assertEquals("CN=Test CA,C=CA", TestConstants.CERT_GENERATED.getIssuer().toString()); assertEquals(TestConstants.NOW, TestConstants.CERT_GENERATED.getValidity().getNotBefore().getTimestamp()); assertEquals(TestConstants.NOW.plusYears(1), diff --git a/src/test/model/pki/cert/ValidityTest.java b/src/test/model/pki/cert/ValidityTest.java index 2471ced..0eb60b5 100644 --- a/src/test/model/pki/cert/ValidityTest.java +++ b/src/test/model/pki/cert/ValidityTest.java @@ -109,7 +109,7 @@ public class ValidityTest { final Byte[] utcBytes = utc.encodeDER(); final Byte[] genBytes = gen.encodeDER(); - assertArrayEquals(Stream.of(Arrays.asList(new Byte[]{0x30, (byte) (utcBytes.length + genBytes.length)}), + assertArrayEquals(Stream.of(Arrays.asList((byte) 0x30, (byte) (utcBytes.length + genBytes.length)), Arrays.asList(utcBytes), Arrays.asList(genBytes)) .flatMap(Collection::stream) diff --git a/src/test/ui/UtilsTest.java b/src/test/ui/UtilsTest.java index 0b22286..3a6c885 100644 --- a/src/test/ui/UtilsTest.java +++ b/src/test/ui/UtilsTest.java @@ -39,29 +39,29 @@ public class UtilsTest { @Test void testParsePEM() throws ParseException { assertArrayEquals(IntStream.range(0, 48).mapToObj(i -> (byte) 1).toArray(Byte[]::new), - Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----\n" + - "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n" + - "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), + Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----\n" + + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n" + + "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), "BLABLA")); assertThrows(ParseException.class, () -> { - Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----\n" + - "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n" + - "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), + Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----\n" + + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n" + + "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), "LALA"); }); assertThrows(ParseException.class, () -> { - Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----" + - "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB" + - "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), + Utils.parsePEM(Utils.byteToByte(("-----BEGIN BLABLA-----" + + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB" + + "-----END BLABLA-----").getBytes(StandardCharsets.UTF_8)), "BLABLA"); }); } @Test void testToPEM() { - assertEquals("-----BEGIN ABC-----\n" + - "AQ==\n" + - "-----END ABC-----", + assertEquals("-----BEGIN ABC-----\n" + + "AQ==\n" + + "-----END ABC-----", Utils.toPEM(new Byte[]{0x1}, "ABC")); } } diff --git a/tests/.gitignore b/tests/.gitignore index 507d98f..b44dda2 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,6 +1,11 @@ *.key *.crt -*.txt* +index.txt +index.txt.old +index.txt.attr.old *.csr newcerts/ +subcrts/ serial +serial.old +*.tar diff --git a/tests/Makefile b/tests/Makefile index 6a294b6..545114f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,6 +6,22 @@ leaf.csr: leaf.key leaf.csr.cnf leaf.key: openssl genrsa -out leaf.key 4096 +sub.crt.tar: sub.csr ca.cnf + rm -rf subcrts + mkdir subcrts + mkdir -p newcerts + touch index.txt + EXT=normal; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=basic_constraints_no; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=basic_constraints_wrong; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=key_usage_missing; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=key_usage_wrong_1; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=key_usage_wrong_2; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + EXT=key_usage_wrong_3; openssl ca -batch -config ca.cnf -extensions extensions_sub_$$EXT -notext -rand_serial -in sub.csr -out subcrts/$$EXT.crt + openssl ca -batch -config ca.cnf -notext -rand_serial -in sub.csr -out subcrts/v1.crt + tar cvf sub.crt.tar subcrts/ + rm -rf subcrts/ + sub.crt: sub.csr mkdir -p newcerts touch index.txt @@ -37,4 +53,4 @@ crlnumber: reset: echo "!!! THIS WILL RESET EVERYTHING, INCLUDING PRIVATE KEYS !!!" # sleep 5 - rm -rf newcerts serial index.txt* private certs sub.csr crlnumber* ca.crl ca.crt sub.crt ca.key + rm -rf newcerts serial index.txt private certs sub.csr crlnumber* ca.crl ca.crt sub.crt ca.key index.txt.old subcrts/ diff --git a/tests/ca.cnf b/tests/ca.cnf index ef5a9c9..9c034cc 100644 --- a/tests/ca.cnf +++ b/tests/ca.cnf @@ -15,13 +15,6 @@ RANDFILE = $dir/.rand private_key = $dir/ca.key certificate = $dir/ca.crt -# CRL -crlnumber = $dir/crlnumber -crl = $dir/ca.crl -crl_extensions = crl_ext -# Root CA CRL: 1 year -default_crl_days = 365 - # Cryptography default_md = sha512 @@ -54,55 +47,54 @@ x509_extensions = extensions [ req_dn ] commonName = Common Name countryName = Country Name (2 letter code) -# For simplicity -#stateOrProvinceName = State or Province Name -#localityName = Locality Name -#0.organizationName = Organization Name -# CAB Baseline (BR) v2.0.0 -# OU name must not present -# Email address is not recommended (as per Jimmy) -#organizationalUnitName = Organizational Unit Name -#emailAddress = Email Address - commonName_default = Test Root CA countryName_default = CA -#stateOrProvinceName_default = British Columbia -#localityName_default = Vancouver -#0.organizationName_default = Yuuta Home -#organizationalUnitName_default = IT -#emailAddress_default = yuuta@yuuta.moe [ extensions ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign -# Seems like it is completely unnecessary to put CRL and AIA in RootCA -# because they point to the issuer's info. -# crlDistributionPoints = crldp -# Because I don't have a real OID -#certificatePolicies = @polset -# Seems like it is unnecessary. -#authorityInfoAccess = caIssuers;URI:http://home.yuuta.moe/pki/rootca.crt - -[ extensions_sub ] + +[ extensions_sub_normal ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ extensions_sub_basic_constraints_no ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer -basicConstraints = critical, CA:true, pathlen: 0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign -crlDistributionPoints = crldp -authorityInfoAccess = caIssuers;URI:http://home.yuuta.moe/pki/rootca.crt - -#[ polset ] -#policyIdentifier = 1.3.6.1.4.1.191981.5.1.1 -#CPS.1 = "http://home.yuuta.moe/pki/policy" -#userNotice.1 = @polset_notice -# -#[ polset_notice ] -#explicitText = "This certificate authority is for internal use only." - -[ crldp ] -fullname = URI:http://home.yuuta.moe/pki/rootca.crl - -[ crl_ext ] -authorityKeyIdentifier = keyid:always + +[ extensions_sub_basic_constraints_wrong ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:false +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ extensions_sub_key_usage_missing ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true + +[ extensions_sub_key_usage_wrong_1 ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +# No digitalSignature +keyUsage = critical, cRLSign, keyCertSign + +[ extensions_sub_key_usage_wrong_2 ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +# No cRLSign +keyUsage = critical, digitalSignature, keyCertSign + +[ extensions_sub_key_usage_wrong_3 ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +# No keyCertSign +keyUsage = critical, digitalSignature, cRLSign diff --git a/tests/index.txt.attr b/tests/index.txt.attr new file mode 100644 index 0000000..3a7e39e --- /dev/null +++ b/tests/index.txt.attr @@ -0,0 +1 @@ +unique_subject = no |