aboutsummaryrefslogtreecommitdiff
path: root/src/main/model/pki/cert/TbsCertificate.java
diff options
context:
space:
mode:
authorYuuta Liang <yuutaw@students.cs.ubc.ca>2023-10-12 12:10:33 +0800
committerYuuta Liang <yuutaw@students.cs.ubc.ca>2023-10-12 12:10:33 +0800
commitd342a45d98c4795b3a3fe1aaef5236ad4a782b55 (patch)
treef4ebc0ad962b138d9371413fcc71c97a559df506 /src/main/model/pki/cert/TbsCertificate.java
parente60c9c76243cfe0a408af98dc60bedb973e815db (diff)
downloadjca-d342a45d98c4795b3a3fe1aaef5236ad4a782b55.tar
jca-d342a45d98c4795b3a3fe1aaef5236ad4a782b55.tar.gz
jca-d342a45d98c4795b3a3fe1aaef5236ad4a782b55.tar.bz2
jca-d342a45d98c4795b3a3fe1aaef5236ad4a782b55.zip
Implement data structures from X.680, X.501, X.509, and PKCS#10, with X.690 encoding / decoding support
The implementation took four days, and it is still a little bit rough. Updated version should arrive soon. Signed-off-by: Yuuta Liang <yuutaw@students.cs.ubc.ca>
Diffstat (limited to 'src/main/model/pki/cert/TbsCertificate.java')
-rw-r--r--src/main/model/pki/cert/TbsCertificate.java263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/main/model/pki/cert/TbsCertificate.java b/src/main/model/pki/cert/TbsCertificate.java
new file mode 100644
index 0000000..1175456
--- /dev/null
+++ b/src/main/model/pki/cert/TbsCertificate.java
@@ -0,0 +1,263 @@
+package model.pki.cert;
+
+import model.asn1.*;
+import model.asn1.exceptions.ParseException;
+import model.asn1.parsing.BytesReader;
+import model.pki.AlgorithmIdentifier;
+import model.pki.SubjectPublicKeyInfo;
+import model.x501.Name;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Stream;
+
+/**
+ * Represents a X.509 certificate
+ *
+ * <pre>
+ * Certificate ::= SIGNED{TBSCertificate}
+ * TBSCertificate ::= SEQUENCE {
+ * version [0] Version DEFAULT v1,
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier{{SupportedAlgorithms}},
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ * ...,
+ * [[2: -- if present, version shall be v2 or v3
+ * subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL]],
+ * [[3: -- if present, version shall be v2 or v3
+ * extensions [3] Extensions OPTIONAL]]
+ * -- If present, version shall be v3]]
+ * }
+ *
+ * Version ::= INTEGER {v1(0), v2(1), v3(2)}
+ * CertificateSerialNumber ::= INTEGER
+ *
+ * uniqueIdentifier ATTRIBUTE ::= {
+ * WITH SYNTAX UniqueIdentifier
+ * EQUALITY MATCHING RULE bitStringMatch
+ * LDAP-SYNTAX bitString.&id
+ * LDAP-NAME {"x500UniqueIdentifier"}
+ * ID id-at-uniqueIdentifier }
+ * UniqueIdentifier ::= BIT STRING
+ * </pre>
+ *
+ * NOTE that subjectUniqueIdentifier and issuerUniqueIdentifier are not supported.
+ */
+public class TbsCertificate extends ASN1Object {
+ // Version ::= INTEGER {v1(0), v2(1), v3(2)}
+ public static final int VERSION_V1 = 0;
+ public static final int VERSION_V2 = 1;
+ public static final int VERSION_V3 = 2;
+
+ /**
+ * The X.509 cert version. subjectUniqueIdentifier is v2 only, and extensions is v3 only.
+ * <pre>
+ * [0] Version DEFAULT v1
+ * </pre>
+ */
+ private final Int version;
+
+ /**
+ * The serial number of that certificate that is unique across the CA.
+ * <pre>
+ * serialNumber CertificateSerialNumber
+ * CertificateSerialNumber ::= INTEGER
+ * </pre>
+ */
+ private final Int serialNumber;
+
+ private final AlgorithmIdentifier signature;
+
+ /**
+ * The subject and issuer distinguished names.
+ * <pre>
+ * issuer Name,
+ * subject Name
+ * </pre>
+ */
+ private final Name issuer;
+
+ /**
+ * The validity period of that certificate.
+ * Validity ::= SEQUENCE { notBefore Time, notAfter Time, ... }
+ */
+ private final Validity validity;
+
+ /**
+ * See the comments on issuer.
+ */
+ private final Name subject;
+
+ private final SubjectPublicKeyInfo subjectPublicKeyInfo;
+
+ /**
+ * [3] Optional.
+ */
+ private final Extensions extensions;
+
+ /**
+ * EFFECTS: Init with the given parameters. For tag and parentTag, see {@link ASN1Object}.
+ * REQUIRES:
+ * - Version must be V1, V2, or V3.
+ * - {issuer,subject}UniqueIdentifier could be null.
+ * - If {issuer,subject}UniqueIdentifier presents, version must be V2 or V3.
+ * - Extensions could be null.
+ * - If extensions presents, version must be V3.
+ * - The signature should be valid.
+ * - Field and Desired Tags:
+ * version CONTEXT SPECIFIC 0 (EXPLICIT), INTEGER, OPTIONAL DEFAULT v1
+ * serialNumber INTEGER
+ * signature SEQUENCE
+ * issuer SEQUENCE
+ * validity SEQUENCE
+ * subject SEQUENCE
+ * subjectPublicKeyInfo SEQUENCE
+ * extensions CONTEXT SPECIFIC 3 (EXPLICIT), SEQUENCE, OPTIONAL
+ */
+ public TbsCertificate(Tag tag, Tag parentTag,
+ final Int version,
+ final Int serialNumber,
+ final AlgorithmIdentifier signature,
+ final Name issuer,
+ final Validity validity,
+ final Name subject,
+ final SubjectPublicKeyInfo subjectPublicKeyInfo,
+ final Extensions extensions) {
+ super(tag, parentTag);
+ this.version = version;
+ this.serialNumber = serialNumber;
+ this.signature = signature;
+ this.issuer = issuer;
+ this.validity = validity;
+ this.subject = subject;
+ this.subjectPublicKeyInfo = subjectPublicKeyInfo;
+ this.extensions = extensions;
+ }
+
+ /**
+ * EFFECTS: Parse input DER.
+ * Throws {@link ASN1Object} if invalid:
+ * - Any fields missing
+ * - Any fields having an incorrect parent / inner tag (as seen in the ASN.1 definition)
+ * - Any fields with encoding instructions that violate implicit / explicit encoding rules
+ * - extensions are specified, but the version is v1 or v2
+ * - Other issues found during parsing the object, like early EOF (see {@link ASN1Object})
+ * MODIFIES: this, encoded
+ */
+ public TbsCertificate(BytesReader encoded, boolean hasParentTag) throws ParseException {
+ super(encoded, hasParentTag);
+ int i = encoded.getIndex();
+ if (encoded.detectTag(new Tag(TagClass.CONTEXT_SPECIFIC, true, 0))) {
+ this.version = new Int(encoded, true);
+ } else {
+ this.version = null;
+ }
+ this.serialNumber = new Int(encoded, false);
+ this.signature = new AlgorithmIdentifier(encoded, false);
+ this.issuer = new Name(encoded, false);
+ this.validity = new Validity(encoded, false);
+ this.subject = new Name(encoded, false);
+ this.subjectPublicKeyInfo = new SubjectPublicKeyInfo(encoded, false);
+ if (encoded.detectTag(new Tag(TagClass.CONTEXT_SPECIFIC, true, 3))) {
+ this.extensions = new Extensions(encoded, true);
+ } else {
+ // Enforce the extensions tag - nothing else should be here.
+ if (Integer.compareUnsigned(getLength(), (encoded.getIndex() - i)) != 0) {
+ new Tag(encoded).enforce(new Tag(TagClass.CONTEXT_SPECIFIC, true, 3));
+ }
+ this.extensions = null;
+ }
+ enforceInput();
+ enforceVersion();
+ }
+
+ /**
+ * EFFECTS: Throw {@link ParseException} if any field have illegal tags.
+ */
+ private void enforceInput() throws ParseException {
+ if (this.version != null) {
+ this.version.getTag().enforce(Int.TAG);
+ this.version.getParentTag().enforce(new Tag(TagClass.CONTEXT_SPECIFIC, true, 0));
+ }
+ this.serialNumber.getTag().enforce(Int.TAG);
+ this.signature.getTag().enforce(TAG_SEQUENCE);
+ this.issuer.getTag().enforce(TAG_SEQUENCE);
+ this.validity.getTag().enforce(TAG_SEQUENCE);
+ this.subject.getTag().enforce(TAG_SEQUENCE);
+ this.subjectPublicKeyInfo.getTag().enforce(TAG_SEQUENCE);
+ if (extensions != null) {
+ this.extensions.getTag().enforce(TAG_SEQUENCE);
+ this.extensions.getParentTag().enforce(new Tag(TagClass.CONTEXT_SPECIFIC, true, 3));
+ }
+ }
+
+ /**
+ * EFFECTS: Throw {@link ParseException} if the version is incorrect.
+ */
+ private void enforceVersion() throws ParseException {
+ if (version != null
+ && (version.getLong() != VERSION_V1
+ && version.getLong() != VERSION_V2
+ && version.getLong() != VERSION_V3)) {
+ throw new ParseException("Illegal certificate version: " + version.getLong());
+ }
+ if (extensions != null && (version == null || version.getLong() != VERSION_V3)) {
+ throw new ParseException("Extensions present. The version must be v3 or above.");
+ }
+ }
+
+ /**
+ * EFFECTS: Encode into ordered DER.
+ */
+ @Override
+ public Byte[] encodeValueDER() {
+ return Stream.of(version == null ? Collections.<Byte>emptyList() : Arrays.asList(version.encodeDER()),
+ Arrays.asList(serialNumber.encodeDER()),
+ Arrays.asList(signature.encodeDER()),
+ Arrays.asList(issuer.encodeDER()),
+ Arrays.asList(validity.encodeDER()),
+ Arrays.asList(subject.encodeDER()),
+ Arrays.asList(subjectPublicKeyInfo.encodeDER()),
+ extensions == null ? Collections.<Byte>emptyList()
+ : Arrays.asList(extensions.encodeDER()))
+ .flatMap(Collection::stream)
+ .toArray(Byte[]::new);
+ }
+
+ public Int getVersion() {
+ return version;
+ }
+
+ public Int getSerialNumber() {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature() {
+ return signature;
+ }
+
+ public Name getIssuer() {
+ return issuer;
+ }
+
+ public Validity getValidity() {
+ return validity;
+ }
+
+ public Name getSubject() {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo() {
+ return subjectPublicKeyInfo;
+ }
+
+ public Extensions getExtensions() {
+ return extensions;
+ }
+}