SslCertificate.java revision 6da00334478df64921b68fcbb45c9d1eef6f35bd
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.content.Context; 20import android.os.Bundle; 21import android.text.format.DateFormat; 22import android.view.LayoutInflater; 23import android.view.View; 24import android.widget.TextView; 25 26import java.security.cert.X509Certificate; 27import java.text.ParseException; 28import java.text.SimpleDateFormat; 29import java.util.Date; 30import java.util.Vector; 31 32import com.android.org.bouncycastle.asn1.DERObjectIdentifier; 33import com.android.org.bouncycastle.asn1.x509.X509Name; 34 35/** 36 * SSL certificate info (certificate details) class 37 */ 38public class SslCertificate { 39 40 /** 41 * SimpleDateFormat pattern for an ISO 8601 date 42 */ 43 private static String ISO_8601_DATE_FORMAT = "yyyy-MM-dd HH:mm:ssZ"; 44 45 /** 46 * Name of the entity this certificate is issued to 47 */ 48 private DName mIssuedTo; 49 50 /** 51 * Name of the entity this certificate is issued by 52 */ 53 private DName mIssuedBy; 54 55 /** 56 * Not-before date from the validity period 57 */ 58 private Date mValidNotBefore; 59 60 /** 61 * Not-after date from the validity period 62 */ 63 private Date mValidNotAfter; 64 65 /** 66 * Bundle key names 67 */ 68 private static final String ISSUED_TO = "issued-to"; 69 private static final String ISSUED_BY = "issued-by"; 70 private static final String VALID_NOT_BEFORE = "valid-not-before"; 71 private static final String VALID_NOT_AFTER = "valid-not-after"; 72 73 /** 74 * Saves the certificate state to a bundle 75 * @param certificate The SSL certificate to store 76 * @return A bundle with the certificate stored in it or null if fails 77 */ 78 public static Bundle saveState(SslCertificate certificate) { 79 Bundle bundle = null; 80 81 if (certificate != null) { 82 bundle = new Bundle(); 83 84 bundle.putString(ISSUED_TO, certificate.getIssuedTo().getDName()); 85 bundle.putString(ISSUED_BY, certificate.getIssuedBy().getDName()); 86 87 bundle.putString(VALID_NOT_BEFORE, certificate.getValidNotBefore()); 88 bundle.putString(VALID_NOT_AFTER, certificate.getValidNotAfter()); 89 } 90 91 return bundle; 92 } 93 94 /** 95 * Restores the certificate stored in the bundle 96 * @param bundle The bundle with the certificate state stored in it 97 * @return The SSL certificate stored in the bundle or null if fails 98 */ 99 public static SslCertificate restoreState(Bundle bundle) { 100 if (bundle != null) { 101 return new SslCertificate( 102 bundle.getString(ISSUED_TO), 103 bundle.getString(ISSUED_BY), 104 bundle.getString(VALID_NOT_BEFORE), 105 bundle.getString(VALID_NOT_AFTER)); 106 } 107 108 return null; 109 } 110 111 /** 112 * Creates a new SSL certificate object 113 * @param issuedTo The entity this certificate is issued to 114 * @param issuedBy The entity that issued this certificate 115 * @param validNotBefore The not-before date from the certificate 116 * validity period in ISO 8601 format 117 * @param validNotAfter The not-after date from the certificate 118 * validity period in ISO 8601 format 119 * @deprecated Use {@link #SslCertificate(X509Certificate)} 120 */ 121 @Deprecated 122 public SslCertificate( 123 String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) { 124 this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter)); 125 } 126 127 /** 128 * Creates a new SSL certificate object 129 * @param issuedTo The entity this certificate is issued to 130 * @param issuedBy The entity that issued this certificate 131 * @param validNotBefore The not-before date from the certificate validity period 132 * @param validNotAfter The not-after date from the certificate validity period 133 * @deprecated Use {@link #SslCertificate(X509Certificate)} 134 */ 135 @Deprecated 136 public SslCertificate( 137 String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) { 138 mIssuedTo = new DName(issuedTo); 139 mIssuedBy = new DName(issuedBy); 140 mValidNotBefore = cloneDate(validNotBefore); 141 mValidNotAfter = cloneDate(validNotAfter); 142 } 143 144 /** 145 * Creates a new SSL certificate object from an X509 certificate 146 * @param certificate X509 certificate 147 */ 148 public SslCertificate(X509Certificate certificate) { 149 this(certificate.getSubjectDN().getName(), 150 certificate.getIssuerDN().getName(), 151 certificate.getNotBefore(), 152 certificate.getNotAfter()); 153 } 154 155 /** 156 * @return Not-before date from the certificate validity period or 157 * "" if none has been set 158 */ 159 public Date getValidNotBeforeDate() { 160 return cloneDate(mValidNotBefore); 161 } 162 163 /** 164 * @return Not-before date from the certificate validity period in 165 * ISO 8601 format or "" if none has been set 166 * 167 * @deprecated Use {@link #getValidNotBeforeDate()} 168 */ 169 @Deprecated 170 public String getValidNotBefore() { 171 return formatDate(mValidNotBefore); 172 } 173 174 /** 175 * @return Not-after date from the certificate validity period or 176 * "" if none has been set 177 */ 178 public Date getValidNotAfterDate() { 179 return cloneDate(mValidNotAfter); 180 } 181 182 /** 183 * @return Not-after date from the certificate validity period in 184 * ISO 8601 format or "" if none has been set 185 * 186 * @deprecated Use {@link #getValidNotAfterDate()} 187 */ 188 @Deprecated 189 public String getValidNotAfter() { 190 return formatDate(mValidNotAfter); 191 } 192 193 /** 194 * @return Issued-to distinguished name or null if none has been set 195 */ 196 public DName getIssuedTo() { 197 return mIssuedTo; 198 } 199 200 /** 201 * @return Issued-by distinguished name or null if none has been set 202 */ 203 public DName getIssuedBy() { 204 return mIssuedBy; 205 } 206 207 /** 208 * @return A string representation of this certificate for debugging 209 */ 210 public String toString() { 211 return ("Issued to: " + mIssuedTo.getDName() + ";\n" 212 + "Issued by: " + mIssuedBy.getDName() + ";\n"); 213 } 214 215 /** 216 * Parse an ISO 8601 date converting ParseExceptions to a null result; 217 */ 218 private static Date parseDate(String string) { 219 try { 220 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).parse(string); 221 } catch (ParseException e) { 222 return null; 223 } 224 } 225 226 /** 227 * Format a date as an ISO 8601 string, return "" for a null date 228 */ 229 private static String formatDate(Date date) { 230 if (date == null) { 231 return ""; 232 } 233 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).format(date); 234 } 235 236 /** 237 * Clone a possibly null Date 238 */ 239 private static Date cloneDate(Date date) { 240 if (date == null) { 241 return null; 242 } 243 return (Date) date.clone(); 244 } 245 246 /** 247 * A distinguished name helper class: a 3-tuple of: 248 * - common name (CN), 249 * - organization (O), 250 * - organizational unit (OU) 251 */ 252 public class DName { 253 /** 254 * Distinguished name (normally includes CN, O, and OU names) 255 */ 256 private String mDName; 257 258 /** 259 * Common-name (CN) component of the name 260 */ 261 private String mCName; 262 263 /** 264 * Organization (O) component of the name 265 */ 266 private String mOName; 267 268 /** 269 * Organizational Unit (OU) component of the name 270 */ 271 private String mUName; 272 273 /** 274 * Creates a new distinguished name 275 * @param dName The distinguished name 276 */ 277 public DName(String dName) { 278 if (dName != null) { 279 mDName = dName; 280 try { 281 X509Name x509Name = new X509Name(dName); 282 283 Vector val = x509Name.getValues(); 284 Vector oid = x509Name.getOIDs(); 285 286 for (int i = 0; i < oid.size(); i++) { 287 if (oid.elementAt(i).equals(X509Name.CN)) { 288 mCName = (String) val.elementAt(i); 289 continue; 290 } 291 292 if (oid.elementAt(i).equals(X509Name.O)) { 293 mOName = (String) val.elementAt(i); 294 continue; 295 } 296 297 if (oid.elementAt(i).equals(X509Name.OU)) { 298 mUName = (String) val.elementAt(i); 299 continue; 300 } 301 } 302 } catch (IllegalArgumentException ex) { 303 // thrown if there is an error parsing the string 304 } 305 } 306 } 307 308 /** 309 * @return The distinguished name (normally includes CN, O, and OU names) 310 */ 311 public String getDName() { 312 return mDName != null ? mDName : ""; 313 } 314 315 /** 316 * @return The Common-name (CN) component of this name 317 */ 318 public String getCName() { 319 return mCName != null ? mCName : ""; 320 } 321 322 /** 323 * @return The Organization (O) component of this name 324 */ 325 public String getOName() { 326 return mOName != null ? mOName : ""; 327 } 328 329 /** 330 * @return The Organizational Unit (OU) component of this name 331 */ 332 public String getUName() { 333 return mUName != null ? mUName : ""; 334 } 335 } 336 337 /** 338 * Inflates the SSL certificate view (helper method). 339 * @return The resultant certificate view with issued-to, issued-by, 340 * issued-on, expires-on, and possibly other fields set. 341 * If the input certificate is null, returns null. 342 * 343 * @hide Used by Browser and Settings 344 */ 345 public View inflateCertificateView(Context context) { 346 LayoutInflater factory = LayoutInflater.from(context); 347 348 View certificateView = factory.inflate( 349 com.android.internal.R.layout.ssl_certificate, null); 350 351 // issued to: 352 SslCertificate.DName issuedTo = getIssuedTo(); 353 if (issuedTo != null) { 354 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_common)) 355 .setText(issuedTo.getCName()); 356 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_org)) 357 .setText(issuedTo.getOName()); 358 ((TextView) certificateView.findViewById(com.android.internal.R.id.to_org_unit)) 359 .setText(issuedTo.getUName()); 360 } 361 362 // issued by: 363 SslCertificate.DName issuedBy = getIssuedBy(); 364 if (issuedBy != null) { 365 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_common)) 366 .setText(issuedBy.getCName()); 367 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_org)) 368 .setText(issuedBy.getOName()); 369 ((TextView) certificateView.findViewById(com.android.internal.R.id.by_org_unit)) 370 .setText(issuedBy.getUName()); 371 } 372 373 // issued on: 374 String issuedOn = formatCertificateDate(context, getValidNotBeforeDate()); 375 ((TextView) certificateView.findViewById(com.android.internal.R.id.issued_on)) 376 .setText(issuedOn); 377 378 // expires on: 379 String expiresOn = formatCertificateDate(context, getValidNotAfterDate()); 380 ((TextView) certificateView.findViewById(com.android.internal.R.id.expires_on)) 381 .setText(expiresOn); 382 383 return certificateView; 384 } 385 386 /** 387 * Formats the certificate date to a properly localized date string. 388 * @return Properly localized version of the certificate date string and 389 * the "" if it fails to localize. 390 */ 391 private String formatCertificateDate(Context context, Date certificateDate) { 392 if (certificateDate == null) { 393 return ""; 394 } 395 return DateFormat.getDateFormat(context).format(certificateDate); 396 } 397} 398