1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Alexander Y. Kleymenov
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.x509;
24
25import java.io.IOException;
26import java.math.BigInteger;
27import java.util.Arrays;
28import java.util.Date;
29import java.util.List;
30import javax.security.auth.x500.X500Principal;
31import org.apache.harmony.security.asn1.ASN1Explicit;
32import org.apache.harmony.security.asn1.ASN1Integer;
33import org.apache.harmony.security.asn1.ASN1Sequence;
34import org.apache.harmony.security.asn1.ASN1SequenceOf;
35import org.apache.harmony.security.asn1.ASN1Type;
36import org.apache.harmony.security.asn1.BerInputStream;
37import org.apache.harmony.security.x501.Name;
38
39
40/**
41 * The class encapsulates the ASN.1 DER encoding/decoding work
42 * with TBSCertList structure which is the part of X.509 CRL
43 * (as specified in RFC 3280 -
44 *  Internet X.509 Public Key Infrastructure.
45 *  Certificate and Certificate Revocation List (CRL) Profile.
46 *  http://www.ietf.org/rfc/rfc3280.txt):
47 *
48 * <pre>
49 *   TBSCertList  ::=  SEQUENCE  {
50 *        version                 Version OPTIONAL,
51 *                                     -- if present, MUST be v2
52 *        signature               AlgorithmIdentifier,
53 *        issuer                  Name,
54 *        thisUpdate              Time,
55 *        nextUpdate              Time OPTIONAL,
56 *        revokedCertificates     SEQUENCE OF SEQUENCE  {
57 *             userCertificate         CertificateSerialNumber,
58 *             revocationDate          Time,
59 *             crlEntryExtensions      Extensions OPTIONAL
60 *                                           -- if present, MUST be v2
61 *                                  }  OPTIONAL,
62 *        crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
63 *                                           -- if present, MUST be v2
64 *   }
65 * </pre>
66 */
67public final class TBSCertList {
68    /** the value of version field of the structure */
69    private final int version;
70    /** the value of signature field of the structure */
71    private final AlgorithmIdentifier signature;
72    /** the value of issuer field of the structure */
73    private final Name issuer;
74    /** the value of thisUpdate of the structure */
75    private final Date thisUpdate;
76    /** the value of nextUpdate of the structure */
77    private final Date nextUpdate;
78    /** the value of revokedCertificates of the structure */
79    private final List<RevokedCertificate> revokedCertificates;
80    /** the value of crlExtensions field of the structure */
81    private final Extensions crlExtensions;
82    /** the ASN.1 encoded form of TBSCertList */
83    private byte[] encoding;
84
85    public static class RevokedCertificate {
86        private final BigInteger userCertificate;
87        private final Date revocationDate;
88        private final Extensions crlEntryExtensions;
89
90        private boolean issuerRetrieved;
91        private X500Principal issuer;
92        private byte[] encoding;
93
94        public RevokedCertificate(BigInteger userCertificate,
95                Date revocationDate, Extensions crlEntryExtensions) {
96            this.userCertificate = userCertificate;
97            this.revocationDate = revocationDate;
98            this.crlEntryExtensions = crlEntryExtensions;
99        }
100
101        public Extensions getCrlEntryExtensions() {
102            return crlEntryExtensions;
103        }
104
105        public BigInteger getUserCertificate() {
106            return userCertificate;
107        }
108
109        public Date getRevocationDate() {
110            return revocationDate;
111        }
112
113        /**
114         * Returns the value of Certificate Issuer Extension, if it is
115         * presented.
116         */
117        public X500Principal getIssuer() {
118            if (crlEntryExtensions == null) {
119                return null;
120            }
121            if (!issuerRetrieved) {
122                try {
123                    issuer =
124                        crlEntryExtensions.valueOfCertificateIssuerExtension();
125                } catch (IOException e) {
126                    e.printStackTrace();
127                }
128                issuerRetrieved = true;
129            }
130            return issuer;
131        }
132
133        public byte[] getEncoded() {
134            if (encoding == null) {
135                encoding = ASN1.encode(this);
136            }
137            return encoding;
138        }
139
140        public boolean equals(Object rc) {
141            if (!(rc instanceof RevokedCertificate)) {
142                return false;
143            }
144            RevokedCertificate rcert = (RevokedCertificate) rc;
145            return userCertificate.equals(rcert.userCertificate)
146                && ((revocationDate.getTime() / 1000)
147                        == (rcert.revocationDate.getTime() / 1000))
148                && ((crlEntryExtensions == null)
149                    ? rcert.crlEntryExtensions == null
150                    : crlEntryExtensions.equals(rcert.crlEntryExtensions));
151        }
152
153        public int hashCode() {
154            return userCertificate.hashCode() * 37 + (int)revocationDate.getTime() / 1000
155                    + (crlEntryExtensions == null ? 0 : crlEntryExtensions.hashCode());
156        }
157
158        public void dumpValue(StringBuilder sb, String prefix) {
159            sb.append(prefix).append("Certificate Serial Number: ").append(userCertificate).append('\n');
160            sb.append(prefix).append("Revocation Date: ").append(revocationDate);
161            if (crlEntryExtensions != null) {
162                sb.append('\n').append(prefix).append("CRL Entry Extensions: [");
163                crlEntryExtensions.dumpValue(sb, prefix + "  ");
164                sb.append(prefix).append(']');
165            }
166        }
167
168        public static final ASN1Sequence ASN1 = new ASN1Sequence(
169                new ASN1Type[] {ASN1Integer.getInstance(), Time.ASN1,
170                Extensions.ASN1}) {
171            {
172                setOptional(2);
173            }
174
175            @Override protected Object getDecodedObject(BerInputStream in) {
176                Object[] values = (Object[]) in.content;
177                return new RevokedCertificate(
178                            new BigInteger((byte[]) values[0]),
179                            (Date) values[1],
180                            (Extensions) values[2]
181                        );
182            }
183
184            @Override protected void getValues(Object object, Object[] values) {
185                RevokedCertificate rcert = (RevokedCertificate) object;
186                values[0] = rcert.userCertificate.toByteArray();
187                values[1] = rcert.revocationDate;
188                values[2] = rcert.crlEntryExtensions;
189            }
190        };
191    }
192
193    /** Constructs the object with associated ASN.1 encoding */
194    private TBSCertList(int version, AlgorithmIdentifier signature,
195            Name issuer, Date thisUpdate, Date nextUpdate,
196            List<RevokedCertificate> revokedCertificates, Extensions crlExtensions,
197            byte[] encoding) {
198        this.version = version;
199        this.signature = signature;
200        this.issuer = issuer;
201        this.thisUpdate = thisUpdate;
202        this.nextUpdate = nextUpdate;
203        this.revokedCertificates = revokedCertificates;
204        this.crlExtensions = crlExtensions;
205        this.encoding = encoding;
206    }
207
208    /**
209     * Returns the value of version field of the structure.
210     */
211    public int getVersion() {
212        return version;
213    }
214
215    /**
216     * Returns the value of signature field of the structure.
217     */
218    public AlgorithmIdentifier getSignature() {
219        return signature;
220    }
221
222    /**
223     * Returns the value of issuer field of the structure.
224     */
225    public Name getIssuer() {
226        return issuer;
227    }
228
229    /**
230     * Returns the value of thisUpdate field of the structure.
231     */
232    public Date getThisUpdate() {
233        return thisUpdate;
234    }
235
236    /**
237     * Returns the value of nextUpdate field of the structure.
238     */
239    public Date getNextUpdate() {
240        return nextUpdate;
241    }
242
243    /**
244     * Returns the value of revokedCertificates field of the structure.
245     */
246    public List<RevokedCertificate> getRevokedCertificates() {
247        return revokedCertificates;
248    }
249
250    /**
251     * Returns the value of crlExtensions field of the structure.
252     */
253    public Extensions getCrlExtensions() {
254        return crlExtensions;
255    }
256
257    /**
258     * Returns ASN.1 encoded form of this X.509 TBSCertList value.
259     */
260    public byte[] getEncoded() {
261        if (encoding == null) {
262            encoding = ASN1.encode(this);
263        }
264        return encoding;
265    }
266
267    @Override public boolean equals(Object other) {
268        if (!(other instanceof TBSCertList)) {
269            return false;
270        }
271        TBSCertList that = (TBSCertList) other;
272        return version == that.version
273            && signature.equals(that.signature)
274            && Arrays.equals(issuer.getEncoded(), that.issuer.getEncoded())
275            && thisUpdate.getTime() / 1000
276                    == that.thisUpdate.getTime() / 1000
277            && (nextUpdate == null
278                    ? that.nextUpdate == null
279                    : nextUpdate.getTime() / 1000
280                        == that.nextUpdate.getTime() / 1000)
281            && ((revokedCertificates == null || that.revokedCertificates == null)
282                && revokedCertificates == that.revokedCertificates
283                || revokedCertificates.equals(that.revokedCertificates))
284            && (crlExtensions == null
285                    ? that.crlExtensions == null
286                    : crlExtensions.equals(that.crlExtensions));
287    }
288
289    @Override public int hashCode() {
290        return ((version * 37 + signature.hashCode()) * 37
291                + Arrays.hashCode(issuer.getEncoded())) * 37
292                + (int)thisUpdate.getTime() / 1000;
293    }
294
295    public void dumpValue(StringBuilder sb) {
296        sb.append("X.509 CRL v").append(version);
297        sb.append("\nSignature Algorithm: [");
298        signature.dumpValue(sb);
299        sb.append(']');
300        sb.append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253));
301        sb.append("\n\nThis Update: ").append(thisUpdate);
302        sb.append("\nNext Update: ").append(nextUpdate).append('\n');
303        if (revokedCertificates != null) {
304            sb.append("\nRevoked Certificates: ").append(revokedCertificates.size()).append(" [");
305            int number = 1;
306            for (RevokedCertificate revokedCertificate : revokedCertificates) {
307                sb.append("\n  [").append(number++).append(']');
308                revokedCertificate.dumpValue(sb, "  ");
309                sb.append('\n');
310            }
311            sb.append("]\n");
312        }
313        if (crlExtensions != null) {
314            sb.append("\nCRL Extensions: ").append(crlExtensions.size()).append(" [");
315            crlExtensions.dumpValue(sb, "  ");
316            sb.append("]\n");
317        }
318    }
319
320    /**
321     * X.509 TBSCertList encoder/decoder.
322     */
323    public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
324            ASN1Integer.getInstance(), // version
325            AlgorithmIdentifier.ASN1,  // signature
326            Name.ASN1, // issuer
327            Time.ASN1, // thisUpdate
328            Time.ASN1, // nextUpdate
329            new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates
330            new ASN1Explicit(0, Extensions.ASN1) // crlExtensions
331                }) {
332        {
333            setOptional(0);
334            setOptional(4);
335            setOptional(5);
336            setOptional(6);
337        }
338
339        @Override protected Object getDecodedObject(BerInputStream in) throws IOException {
340            Object[] values = (Object[]) in.content;
341            return new TBSCertList(
342                        (values[0] == null)
343                            ? 1
344                            : ASN1Integer.toIntValue(values[0])+1,
345                        (AlgorithmIdentifier) values[1],
346                        (Name) values[2],
347                        (Date) values[3],
348                        (Date) values[4],
349                        (List<RevokedCertificate>) values[5],
350                        (Extensions) values[6],
351                        in.getEncoded()
352                    );
353        }
354
355        @Override protected void getValues(Object object, Object[] values) {
356            TBSCertList tbs = (TBSCertList) object;
357            values[0] = (tbs.version > 1)
358                ? ASN1Integer.fromIntValue(tbs.version - 1) : null;
359            values[1] = tbs.signature;
360            values[2] = tbs.issuer;
361            values[3] = tbs.thisUpdate;
362            values[4] = tbs.nextUpdate;
363            values[5] = tbs.revokedCertificates;
364            values[6] = tbs.crlExtensions;
365        }
366    };
367}
368