package model.pki.cert; import annotations.Assoc; import model.asn1.*; import model.asn1.exceptions.ParseException; import model.asn1.parsing.BytesReader; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.stream.Stream; /** * A X.509v3 certificate extension entry. *
 *     Extension ::= SEQUENCE {
 *      extnId EXTENSION.&id({ExtensionSet}),
 *      critical BOOLEAN DEFAULT FALSE,
 *      extnValue OCTET STRING
 *          (CONTAINING EXTENSION.&ExtnType({ExtensionSet}{@extnId})
 *              ENCODED BY der),
 *     ... }
 * 
* Extensions only exist in v3 certificates. They allow the CA and the relying party to add additional verification * stages to the certificate to constraint its use or to supply additional information. For example, the CA may put a * CDP (CRL Distribution Point) into the extensions. */ public class Extension extends ASN1Object { /** * The ID of the type of that extension. */ @Assoc(partOf = true) private final ObjectIdentifier extnId; /** * Marking an extension critical means that the relying-party * must reject that certificate if the type is unrecognized. * If the type is recognized but cannot be fully parsed, the * behaviour is undefined. * Marking an extension critical reduces compatibility. */ @Assoc(partOf = true, lowerBond = 0) private final Bool critical; /** * The DER-encoded ASN.1 content of that extension. */ @Assoc(partOf = true) private final OctetString extnValue; /** * EFFECTS: Init with tags, extnId, critical, and extnValue. For tags, see {@link ASN1Object}. * extnValue is not checked against extnId. * REQUIRES: Tags of extnId, critical, extnValue should be OID, BOOLEAN, OCTET STRING. The value should be a DER * bytes octet string. If critical is unspecified (which defaults to false), put null. */ public Extension(Tag tag, Tag parentTag, final ObjectIdentifier extnId, final Bool critical, final OctetString extnValue) { super(tag, parentTag); this.extnId = extnId; this.critical = critical; this.extnValue = extnValue; } /** * EFFECTS: Parse input DER. * Throws {@link ParseException} if the input is invalid: * - Any fields missing * - Any fields having an incorrect tag (as seen in the ASN.1 definition) * - Any fields with encoding instructions that violate implicit / explicit encoding rules * - Other issues found during parsing the object, like early EOF (see {@link ASN1Object}) * Note that critical is optional, and if it does not exist, it will be left as null, and it should be treated as * false. * MODIFIES: this, encoded */ public Extension(BytesReader encoded, boolean hasParentType) throws ParseException { super(encoded, hasParentType); this.extnId = new ObjectIdentifier(encoded, false); this.extnId.getTag().enforce(ObjectIdentifier.TAG); if (encoded.detectTag(Bool.TAG)) { critical = new Bool(encoded, false); } else { critical = null; } this.extnValue = new OctetString(encoded, false); this.extnValue.getTag().enforce(OctetString.TAG); } /** * EFFECTS: Encode the DER. */ @Override public Byte[] encodeValueDER() { return Stream.of(Arrays.asList(extnId.encodeDER()), critical == null ? Collections.emptyList() : Arrays.asList(critical.encodeDER()), Arrays.asList(extnValue.encodeDER())) .flatMap(Collection::stream) .toArray(Byte[]::new); } public ObjectIdentifier getExtnId() { return extnId; } public Bool getCritical() { return critical; } public OctetString getExtnValue() { return extnValue; } }