SslCertificate.java revision a5987a510187749c32f0f68f8fc54e1334f91be6
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.http;
18
19import android.os.Bundle;
20
21import java.text.ParseException;
22import java.text.SimpleDateFormat;
23import java.util.Date;
24import java.util.Vector;
25
26import java.security.cert.X509Certificate;
27
28import org.bouncycastle.asn1.DERObjectIdentifier;
29import org.bouncycastle.asn1.x509.X509Name;
30
31/**
32 * SSL certificate info (certificate details) class
33 */
34public class SslCertificate {
35
36    /**
37     * SimpleDateFormat pattern for an ISO 8601 date
38     */
39    private static String ISO_8601_DATE_FORMAT = "yyyy-MM-dd HH:mm:ssZ";
40
41    /**
42     * Name of the entity this certificate is issued to
43     */
44    private DName mIssuedTo;
45
46    /**
47     * Name of the entity this certificate is issued by
48     */
49    private DName mIssuedBy;
50
51    /**
52     * Not-before date from the validity period
53     */
54    private Date mValidNotBefore;
55
56    /**
57     * Not-after date from the validity period
58     */
59    private Date mValidNotAfter;
60
61     /**
62     * Bundle key names
63     */
64    private static final String ISSUED_TO = "issued-to";
65    private static final String ISSUED_BY = "issued-by";
66    private static final String VALID_NOT_BEFORE = "valid-not-before";
67    private static final String VALID_NOT_AFTER = "valid-not-after";
68
69    /**
70     * Saves the certificate state to a bundle
71     * @param certificate The SSL certificate to store
72     * @return A bundle with the certificate stored in it or null if fails
73     */
74    public static Bundle saveState(SslCertificate certificate) {
75        Bundle bundle = null;
76
77        if (certificate != null) {
78            bundle = new Bundle();
79
80            bundle.putString(ISSUED_TO, certificate.getIssuedTo().getDName());
81            bundle.putString(ISSUED_BY, certificate.getIssuedBy().getDName());
82
83            bundle.putString(VALID_NOT_BEFORE, certificate.getValidNotBefore());
84            bundle.putString(VALID_NOT_AFTER, certificate.getValidNotAfter());
85        }
86
87        return bundle;
88    }
89
90    /**
91     * Restores the certificate stored in the bundle
92     * @param bundle The bundle with the certificate state stored in it
93     * @return The SSL certificate stored in the bundle or null if fails
94     */
95    public static SslCertificate restoreState(Bundle bundle) {
96        if (bundle != null) {
97            return new SslCertificate(
98                bundle.getString(ISSUED_TO),
99                bundle.getString(ISSUED_BY),
100                bundle.getString(VALID_NOT_BEFORE),
101                bundle.getString(VALID_NOT_AFTER));
102        }
103
104        return null;
105    }
106
107    /**
108     * Creates a new SSL certificate object
109     * @param issuedTo The entity this certificate is issued to
110     * @param issuedBy The entity that issued this certificate
111     * @param validNotBefore The not-before date from the certificate validity period in ISO 8601 format
112     * @param validNotAfter The not-after date from the certificate validity period in ISO 8601 format
113     * @deprecated Use {@link #SslCertificate(String, String, Date, Date)}
114     */
115    public SslCertificate(
116            String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) {
117        this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter));
118    }
119
120    /**
121     * Creates a new SSL certificate object
122     * @param issuedTo The entity this certificate is issued to
123     * @param issuedBy The entity that issued this certificate
124     * @param validNotBefore The not-before date from the certificate validity period
125     * @param validNotAfter The not-after date from the certificate validity period
126     */
127    public SslCertificate(
128            String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) {
129        mIssuedTo = new DName(issuedTo);
130        mIssuedBy = new DName(issuedBy);
131        mValidNotBefore = cloneDate(validNotBefore);
132        mValidNotAfter  = cloneDate(validNotAfter);
133    }
134
135    /**
136     * Creates a new SSL certificate object from an X509 certificate
137     * @param certificate X509 certificate
138     */
139    public SslCertificate(X509Certificate certificate) {
140        this(certificate.getSubjectDN().getName(),
141             certificate.getIssuerDN().getName(),
142             certificate.getNotBefore(),
143             certificate.getNotAfter());
144    }
145
146    /**
147     * @return Not-before date from the certificate validity period or
148     * "" if none has been set
149     */
150    public Date getValidNotBeforeDate() {
151        return cloneDate(mValidNotBefore);
152    }
153
154    /**
155     * @return Not-before date from the certificate validity period in
156     * ISO 8601 format or "" if none has been set
157     *
158     * @deprecated Use {@link #getValidNotBeforeDate()}
159     */
160    public String getValidNotBefore() {
161        return formatDate(mValidNotBefore);
162    }
163
164    /**
165     * @return Not-after date from the certificate validity period or
166     * "" if none has been set
167     */
168    public Date getValidNotAfterDate() {
169        return cloneDate(mValidNotAfter);
170    }
171
172    /**
173     * @return Not-after date from the certificate validity period in
174     * ISO 8601 format or "" if none has been set
175     *
176     * @deprecated Use {@link #getValidNotAfterDate()}
177     */
178    public String getValidNotAfter() {
179        return formatDate(mValidNotAfter);
180    }
181
182    /**
183     * @return Issued-to distinguished name or null if none has been set
184     */
185    public DName getIssuedTo() {
186        return mIssuedTo;
187    }
188
189    /**
190     * @return Issued-by distinguished name or null if none has been set
191     */
192    public DName getIssuedBy() {
193        return mIssuedBy;
194    }
195
196    /**
197     * @return A string representation of this certificate for debugging
198     */
199    public String toString() {
200        return
201            "Issued to: " + mIssuedTo.getDName() + ";\n" +
202            "Issued by: " + mIssuedBy.getDName() + ";\n";
203    }
204
205    /**
206     * Parse an ISO 8601 date converting ParseExceptions to a null result;
207     */
208    private static Date parseDate(String string) {
209        try {
210            return new SimpleDateFormat(ISO_8601_DATE_FORMAT).parse(string);
211        } catch (ParseException e) {
212            return null;
213        }
214    }
215
216    /**
217     * Format a date as an ISO 8601 string, return "" for a null date
218     */
219    private static String formatDate(Date date) {
220        if (date == null) {
221            return "";
222        }
223        return new SimpleDateFormat(ISO_8601_DATE_FORMAT).format(date);
224    }
225
226    /**
227     * Clone a possibly null Date
228     */
229    private static Date cloneDate(Date date) {
230        if (date == null) {
231            return null;
232        }
233        return (Date) date.clone();
234    }
235
236    /**
237     * A distinguished name helper class: a 3-tuple of:
238     * - common name (CN),
239     * - organization (O),
240     * - organizational unit (OU)
241     */
242    public class DName {
243        /**
244         * Distinguished name (normally includes CN, O, and OU names)
245         */
246        private String mDName;
247
248        /**
249         * Common-name (CN) component of the name
250         */
251        private String mCName;
252
253        /**
254         * Organization (O) component of the name
255         */
256        private String mOName;
257
258        /**
259         * Organizational Unit (OU) component of the name
260         */
261        private String mUName;
262
263        /**
264         * Creates a new distinguished name
265         * @param dName The distinguished name
266         */
267        public DName(String dName) {
268            if (dName != null) {
269                mDName = dName;
270                try {
271                    X509Name x509Name = new X509Name(dName);
272
273                    Vector val = x509Name.getValues();
274                    Vector oid = x509Name.getOIDs();
275
276                    for (int i = 0; i < oid.size(); i++) {
277                        if (oid.elementAt(i).equals(X509Name.CN)) {
278                            mCName = (String) val.elementAt(i);
279                            continue;
280                        }
281
282                        if (oid.elementAt(i).equals(X509Name.O)) {
283                            mOName = (String) val.elementAt(i);
284                            continue;
285                        }
286
287                        if (oid.elementAt(i).equals(X509Name.OU)) {
288                            mUName = (String) val.elementAt(i);
289                            continue;
290                        }
291                    }
292                } catch (IllegalArgumentException ex) {
293                    // thrown if there is an error parsing the string
294                }
295            }
296        }
297
298        /**
299         * @return The distinguished name (normally includes CN, O, and OU names)
300         */
301        public String getDName() {
302            return mDName != null ? mDName : "";
303        }
304
305        /**
306         * @return The Common-name (CN) component of this name
307         */
308        public String getCName() {
309            return mCName != null ? mCName : "";
310        }
311
312        /**
313         * @return The Organization (O) component of this name
314         */
315        public String getOName() {
316            return mOName != null ? mOName : "";
317        }
318
319        /**
320         * @return The Organizational Unit (OU) component of this name
321         */
322        public String getUName() {
323            return mUName != null ? mUName : "";
324        }
325    }
326}
327