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 @Deprecated 116 public SslCertificate( 117 String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) { 118 this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter)); 119 } 120 121 /** 122 * Creates a new SSL certificate object 123 * @param issuedTo The entity this certificate is issued to 124 * @param issuedBy The entity that issued this certificate 125 * @param validNotBefore The not-before date from the certificate validity period 126 * @param validNotAfter The not-after date from the certificate validity period 127 */ 128 public SslCertificate( 129 String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) { 130 mIssuedTo = new DName(issuedTo); 131 mIssuedBy = new DName(issuedBy); 132 mValidNotBefore = cloneDate(validNotBefore); 133 mValidNotAfter = cloneDate(validNotAfter); 134 } 135 136 /** 137 * Creates a new SSL certificate object from an X509 certificate 138 * @param certificate X509 certificate 139 */ 140 public SslCertificate(X509Certificate certificate) { 141 this(certificate.getSubjectDN().getName(), 142 certificate.getIssuerDN().getName(), 143 certificate.getNotBefore(), 144 certificate.getNotAfter()); 145 } 146 147 /** 148 * @return Not-before date from the certificate validity period or 149 * "" if none has been set 150 */ 151 public Date getValidNotBeforeDate() { 152 return cloneDate(mValidNotBefore); 153 } 154 155 /** 156 * @return Not-before date from the certificate validity period in 157 * ISO 8601 format or "" if none has been set 158 * 159 * @deprecated Use {@link #getValidNotBeforeDate()} 160 */ 161 @Deprecated 162 public String getValidNotBefore() { 163 return formatDate(mValidNotBefore); 164 } 165 166 /** 167 * @return Not-after date from the certificate validity period or 168 * "" if none has been set 169 */ 170 public Date getValidNotAfterDate() { 171 return cloneDate(mValidNotAfter); 172 } 173 174 /** 175 * @return Not-after date from the certificate validity period in 176 * ISO 8601 format or "" if none has been set 177 * 178 * @deprecated Use {@link #getValidNotAfterDate()} 179 */ 180 @Deprecated 181 public String getValidNotAfter() { 182 return formatDate(mValidNotAfter); 183 } 184 185 /** 186 * @return Issued-to distinguished name or null if none has been set 187 */ 188 public DName getIssuedTo() { 189 return mIssuedTo; 190 } 191 192 /** 193 * @return Issued-by distinguished name or null if none has been set 194 */ 195 public DName getIssuedBy() { 196 return mIssuedBy; 197 } 198 199 /** 200 * @return A string representation of this certificate for debugging 201 */ 202 public String toString() { 203 return 204 "Issued to: " + mIssuedTo.getDName() + ";\n" + 205 "Issued by: " + mIssuedBy.getDName() + ";\n"; 206 } 207 208 /** 209 * Parse an ISO 8601 date converting ParseExceptions to a null result; 210 */ 211 private static Date parseDate(String string) { 212 try { 213 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).parse(string); 214 } catch (ParseException e) { 215 return null; 216 } 217 } 218 219 /** 220 * Format a date as an ISO 8601 string, return "" for a null date 221 */ 222 private static String formatDate(Date date) { 223 if (date == null) { 224 return ""; 225 } 226 return new SimpleDateFormat(ISO_8601_DATE_FORMAT).format(date); 227 } 228 229 /** 230 * Clone a possibly null Date 231 */ 232 private static Date cloneDate(Date date) { 233 if (date == null) { 234 return null; 235 } 236 return (Date) date.clone(); 237 } 238 239 /** 240 * A distinguished name helper class: a 3-tuple of: 241 * - common name (CN), 242 * - organization (O), 243 * - organizational unit (OU) 244 */ 245 public class DName { 246 /** 247 * Distinguished name (normally includes CN, O, and OU names) 248 */ 249 private String mDName; 250 251 /** 252 * Common-name (CN) component of the name 253 */ 254 private String mCName; 255 256 /** 257 * Organization (O) component of the name 258 */ 259 private String mOName; 260 261 /** 262 * Organizational Unit (OU) component of the name 263 */ 264 private String mUName; 265 266 /** 267 * Creates a new distinguished name 268 * @param dName The distinguished name 269 */ 270 public DName(String dName) { 271 if (dName != null) { 272 mDName = dName; 273 try { 274 X509Name x509Name = new X509Name(dName); 275 276 Vector val = x509Name.getValues(); 277 Vector oid = x509Name.getOIDs(); 278 279 for (int i = 0; i < oid.size(); i++) { 280 if (oid.elementAt(i).equals(X509Name.CN)) { 281 mCName = (String) val.elementAt(i); 282 continue; 283 } 284 285 if (oid.elementAt(i).equals(X509Name.O)) { 286 mOName = (String) val.elementAt(i); 287 continue; 288 } 289 290 if (oid.elementAt(i).equals(X509Name.OU)) { 291 mUName = (String) val.elementAt(i); 292 continue; 293 } 294 } 295 } catch (IllegalArgumentException ex) { 296 // thrown if there is an error parsing the string 297 } 298 } 299 } 300 301 /** 302 * @return The distinguished name (normally includes CN, O, and OU names) 303 */ 304 public String getDName() { 305 return mDName != null ? mDName : ""; 306 } 307 308 /** 309 * @return The Common-name (CN) component of this name 310 */ 311 public String getCName() { 312 return mCName != null ? mCName : ""; 313 } 314 315 /** 316 * @return The Organization (O) component of this name 317 */ 318 public String getOName() { 319 return mOName != null ? mOName : ""; 320 } 321 322 /** 323 * @return The Organizational Unit (OU) component of this name 324 */ 325 public String getUName() { 326 return mUName != null ? mUName : ""; 327 } 328 } 329} 330