aboutsummaryrefslogtreecommitdiff
path: root/src/main/model/pki/cert/Extension.java
blob: 9db83b2671f9d7fba05401287dfc12e9ceb049b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package model.pki.cert;

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.
 * <pre>
 *     Extension ::= SEQUENCE {
 *      extnId EXTENSION.&id({ExtensionSet}),
 *      critical BOOLEAN DEFAULT FALSE,
 *      extnValue OCTET STRING
 *          (CONTAINING EXTENSION.&ExtnType({ExtensionSet}{@extnId})
 *              ENCODED BY der),
 *     ... }
 * </pre>
 * 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.
     */
    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.
     */
    private final Bool critical;

    /**
     * The DER-encoded ASN.1 content of that extension.
     */
    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.<Byte>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;
    }
}