Move to Android N-MR1 SDK.
[android_tools.git] / sdk / sources / android-25 / android / net / http / SslCertificate.java
blob2715af0261df47a9373bf495457f680bafd2624a
1 /*
2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.net.http;
19 import com.android.internal.util.HexDump;
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.text.format.DateFormat;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.widget.TextView;
28 import java.io.ByteArrayInputStream;
29 import java.math.BigInteger;
30 import java.security.MessageDigest;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.cert.Certificate;
33 import java.security.cert.CertificateEncodingException;
34 import java.security.cert.CertificateException;
35 import java.security.cert.CertificateFactory;
36 import java.security.cert.X509Certificate;
37 import java.text.ParseException;
38 import java.text.SimpleDateFormat;
39 import java.util.Date;
40 import java.util.Vector;
42 import com.android.org.bouncycastle.asn1.x509.X509Name;
44 /**
45 * SSL certificate info (certificate details) class
47 public class SslCertificate {
49 /**
50 * SimpleDateFormat pattern for an ISO 8601 date
52 private static String ISO_8601_DATE_FORMAT = "yyyy-MM-dd HH:mm:ssZ";
54 /**
55 * Name of the entity this certificate is issued to
57 private final DName mIssuedTo;
59 /**
60 * Name of the entity this certificate is issued by
62 private final DName mIssuedBy;
64 /**
65 * Not-before date from the validity period
67 private final Date mValidNotBefore;
69 /**
70 * Not-after date from the validity period
72 private final Date mValidNotAfter;
74 /**
75 * The original source certificate, if available.
77 * TODO If deprecated constructors are removed, this should always
78 * be available, and saveState and restoreState can be simplified
79 * to be unconditional.
81 private final X509Certificate mX509Certificate;
83 /**
84 * Bundle key names
86 private static final String ISSUED_TO = "issued-to";
87 private static final String ISSUED_BY = "issued-by";
88 private static final String VALID_NOT_BEFORE = "valid-not-before";
89 private static final String VALID_NOT_AFTER = "valid-not-after";
90 private static final String X509_CERTIFICATE = "x509-certificate";
92 /**
93 * Saves the certificate state to a bundle
94 * @param certificate The SSL certificate to store
95 * @return A bundle with the certificate stored in it or null if fails
97 public static Bundle saveState(SslCertificate certificate) {
98 if (certificate == null) {
99 return null;
101 Bundle bundle = new Bundle();
102 bundle.putString(ISSUED_TO, certificate.getIssuedTo().getDName());
103 bundle.putString(ISSUED_BY, certificate.getIssuedBy().getDName());
104 bundle.putString(VALID_NOT_BEFORE, certificate.getValidNotBefore());
105 bundle.putString(VALID_NOT_AFTER, certificate.getValidNotAfter());
106 X509Certificate x509Certificate = certificate.mX509Certificate;
107 if (x509Certificate != null) {
108 try {
109 bundle.putByteArray(X509_CERTIFICATE, x509Certificate.getEncoded());
110 } catch (CertificateEncodingException ignored) {
113 return bundle;
117 * Restores the certificate stored in the bundle
118 * @param bundle The bundle with the certificate state stored in it
119 * @return The SSL certificate stored in the bundle or null if fails
121 public static SslCertificate restoreState(Bundle bundle) {
122 if (bundle == null) {
123 return null;
125 X509Certificate x509Certificate;
126 byte[] bytes = bundle.getByteArray(X509_CERTIFICATE);
127 if (bytes == null) {
128 x509Certificate = null;
129 } else {
130 try {
131 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
132 Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
133 x509Certificate = (X509Certificate) cert;
134 } catch (CertificateException e) {
135 x509Certificate = null;
138 return new SslCertificate(bundle.getString(ISSUED_TO),
139 bundle.getString(ISSUED_BY),
140 parseDate(bundle.getString(VALID_NOT_BEFORE)),
141 parseDate(bundle.getString(VALID_NOT_AFTER)),
142 x509Certificate);
146 * Creates a new SSL certificate object
147 * @param issuedTo The entity this certificate is issued to
148 * @param issuedBy The entity that issued this certificate
149 * @param validNotBefore The not-before date from the certificate
150 * validity period in ISO 8601 format
151 * @param validNotAfter The not-after date from the certificate
152 * validity period in ISO 8601 format
153 * @deprecated Use {@link #SslCertificate(X509Certificate)}
155 @Deprecated
156 public SslCertificate(
157 String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) {
158 this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter), null);
162 * Creates a new SSL certificate object
163 * @param issuedTo The entity this certificate is issued to
164 * @param issuedBy The entity that issued this certificate
165 * @param validNotBefore The not-before date from the certificate validity period
166 * @param validNotAfter The not-after date from the certificate validity period
167 * @deprecated Use {@link #SslCertificate(X509Certificate)}
169 @Deprecated
170 public SslCertificate(
171 String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) {
172 this(issuedTo, issuedBy, validNotBefore, validNotAfter, null);
176 * Creates a new SSL certificate object from an X509 certificate
177 * @param certificate X509 certificate
179 public SslCertificate(X509Certificate certificate) {
180 this(certificate.getSubjectDN().getName(),
181 certificate.getIssuerDN().getName(),
182 certificate.getNotBefore(),
183 certificate.getNotAfter(),
184 certificate);
187 private SslCertificate(
188 String issuedTo, String issuedBy,
189 Date validNotBefore, Date validNotAfter,
190 X509Certificate x509Certificate) {
191 mIssuedTo = new DName(issuedTo);
192 mIssuedBy = new DName(issuedBy);
193 mValidNotBefore = cloneDate(validNotBefore);
194 mValidNotAfter = cloneDate(validNotAfter);
195 mX509Certificate = x509Certificate;
199 * @return Not-before date from the certificate validity period or
200 * "" if none has been set
202 public Date getValidNotBeforeDate() {
203 return cloneDate(mValidNotBefore);
207 * @return Not-before date from the certificate validity period in
208 * ISO 8601 format or "" if none has been set
210 * @deprecated Use {@link #getValidNotBeforeDate()}
212 @Deprecated
213 public String getValidNotBefore() {
214 return formatDate(mValidNotBefore);
218 * @return Not-after date from the certificate validity period or
219 * "" if none has been set
221 public Date getValidNotAfterDate() {
222 return cloneDate(mValidNotAfter);
226 * @return Not-after date from the certificate validity period in
227 * ISO 8601 format or "" if none has been set
229 * @deprecated Use {@link #getValidNotAfterDate()}
231 @Deprecated
232 public String getValidNotAfter() {
233 return formatDate(mValidNotAfter);
237 * @return Issued-to distinguished name or null if none has been set
239 public DName getIssuedTo() {
240 return mIssuedTo;
244 * @return Issued-by distinguished name or null if none has been set
246 public DName getIssuedBy() {
247 return mIssuedBy;
251 * Convenience for UI presentation, not intended as public API.
253 private static String getSerialNumber(X509Certificate x509Certificate) {
254 if (x509Certificate == null) {
255 return "";
257 BigInteger serialNumber = x509Certificate.getSerialNumber();
258 if (serialNumber == null) {
259 return "";
261 return fingerprint(serialNumber.toByteArray());
265 * Convenience for UI presentation, not intended as public API.
267 private static String getDigest(X509Certificate x509Certificate, String algorithm) {
268 if (x509Certificate == null) {
269 return "";
271 try {
272 byte[] bytes = x509Certificate.getEncoded();
273 MessageDigest md = MessageDigest.getInstance(algorithm);
274 byte[] digest = md.digest(bytes);
275 return fingerprint(digest);
276 } catch (CertificateEncodingException ignored) {
277 return "";
278 } catch (NoSuchAlgorithmException ignored) {
279 return "";
283 private static final String fingerprint(byte[] bytes) {
284 if (bytes == null) {
285 return "";
287 StringBuilder sb = new StringBuilder();
288 for (int i = 0; i < bytes.length; i++) {
289 byte b = bytes[i];
290 HexDump.appendByteAsHex(sb, b, true);
291 if (i+1 != bytes.length) {
292 sb.append(':');
295 return sb.toString();
299 * @return A string representation of this certificate for debugging
301 public String toString() {
302 return ("Issued to: " + mIssuedTo.getDName() + ";\n"
303 + "Issued by: " + mIssuedBy.getDName() + ";\n");
307 * Parse an ISO 8601 date converting ParseExceptions to a null result;
309 private static Date parseDate(String string) {
310 try {
311 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).parse(string);
312 } catch (ParseException e) {
313 return null;
318 * Format a date as an ISO 8601 string, return "" for a null date
320 private static String formatDate(Date date) {
321 if (date == null) {
322 return "";
324 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).format(date);
328 * Clone a possibly null Date
330 private static Date cloneDate(Date date) {
331 if (date == null) {
332 return null;
334 return (Date) date.clone();
338 * A distinguished name helper class: a 3-tuple of:
339 * <ul>
340 * <li>the most specific common name (CN)</li>
341 * <li>the most specific organization (O)</li>
342 * <li>the most specific organizational unit (OU)</li>
343 * <ul>
345 public class DName {
347 * Distinguished name (normally includes CN, O, and OU names)
349 private String mDName;
352 * Common-name (CN) component of the name
354 private String mCName;
357 * Organization (O) component of the name
359 private String mOName;
362 * Organizational Unit (OU) component of the name
364 private String mUName;
367 * Creates a new {@code DName} from a string. The attributes
368 * are assumed to come in most significant to least
369 * significant order which is true of human readable values
370 * returned by methods such as {@code X500Principal.getName()}.
371 * Be aware that the underlying sources of distinguished names
372 * such as instances of {@code X509Certificate} are encoded in
373 * least significant to most significant order, so make sure
374 * the value passed here has the expected ordering of
375 * attributes.
377 public DName(String dName) {
378 if (dName != null) {
379 mDName = dName;
380 try {
381 X509Name x509Name = new X509Name(dName);
383 Vector val = x509Name.getValues();
384 Vector oid = x509Name.getOIDs();
386 for (int i = 0; i < oid.size(); i++) {
387 if (oid.elementAt(i).equals(X509Name.CN)) {
388 if (mCName == null) {
389 mCName = (String) val.elementAt(i);
391 continue;
394 if (oid.elementAt(i).equals(X509Name.O)) {
395 if (mOName == null) {
396 mOName = (String) val.elementAt(i);
397 continue;
401 if (oid.elementAt(i).equals(X509Name.OU)) {
402 if (mUName == null) {
403 mUName = (String) val.elementAt(i);
404 continue;
408 } catch (IllegalArgumentException ex) {
409 // thrown if there is an error parsing the string
415 * @return The distinguished name (normally includes CN, O, and OU names)
417 public String getDName() {
418 return mDName != null ? mDName : "";
422 * @return The most specific Common-name (CN) component of this name
424 public String getCName() {
425 return mCName != null ? mCName : "";
429 * @return The most specific Organization (O) component of this name
431 public String getOName() {
432 return mOName != null ? mOName : "";
436 * @return The most specific Organizational Unit (OU) component of this name
438 public String getUName() {
439 return mUName != null ? mUName : "";
444 * Inflates the SSL certificate view (helper method).
445 * @return The resultant certificate view with issued-to, issued-by,
446 * issued-on, expires-on, and possibly other fields set.
448 * @hide Used by Browser and Settings
450 public View inflateCertificateView(Context context) {
451 LayoutInflater factory = LayoutInflater.from(context);
453 View certificateView = factory.inflate(
454 com.android.internal.R.layout.ssl_certificate, null);
456 // issued to:
457 SslCertificate.DName issuedTo = getIssuedTo();
458 if (issuedTo != null) {
459 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_common))
460 .setText(issuedTo.getCName());
461 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_org))
462 .setText(issuedTo.getOName());
463 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_org_unit))
464 .setText(issuedTo.getUName());
466 // serial number:
467 ((TextView) certificateView.findViewById(com.android.internal.R.id.serial_number))
468 .setText(getSerialNumber(mX509Certificate));
470 // issued by:
471 SslCertificate.DName issuedBy = getIssuedBy();
472 if (issuedBy != null) {
473 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_common))
474 .setText(issuedBy.getCName());
475 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_org))
476 .setText(issuedBy.getOName());
477 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_org_unit))
478 .setText(issuedBy.getUName());
481 // issued on:
482 String issuedOn = formatCertificateDate(context, getValidNotBeforeDate());
483 ((TextView) certificateView.findViewById(com.android.internal.R.id.issued_on))
484 .setText(issuedOn);
486 // expires on:
487 String expiresOn = formatCertificateDate(context, getValidNotAfterDate());
488 ((TextView) certificateView.findViewById(com.android.internal.R.id.expires_on))
489 .setText(expiresOn);
491 // fingerprints:
492 ((TextView) certificateView.findViewById(com.android.internal.R.id.sha256_fingerprint))
493 .setText(getDigest(mX509Certificate, "SHA256"));
494 ((TextView) certificateView.findViewById(com.android.internal.R.id.sha1_fingerprint))
495 .setText(getDigest(mX509Certificate, "SHA1"));
497 return certificateView;
501 * Formats the certificate date to a properly localized date string.
502 * @return Properly localized version of the certificate date string and
503 * the "" if it fails to localize.
505 private String formatCertificateDate(Context context, Date certificateDate) {
506 if (certificateDate == null) {
507 return "";
509 return DateFormat.getDateFormat(context).format(certificateDate);