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 23 package org.apache.harmony.security.provider.cert; 24 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.math.BigInteger; 28 import java.security.InvalidKeyException; 29 import java.security.NoSuchAlgorithmException; 30 import java.security.NoSuchProviderException; 31 import java.security.Principal; 32 import java.security.PublicKey; 33 import java.security.Signature; 34 import java.security.SignatureException; 35 import java.security.cert.CertificateEncodingException; 36 import java.security.cert.CertificateException; 37 import java.security.cert.CertificateExpiredException; 38 import java.security.cert.CertificateNotYetValidException; 39 import java.security.cert.CertificateParsingException; 40 import java.security.cert.X509Certificate; 41 import java.util.Collection; 42 import java.util.Date; 43 import java.util.List; 44 import java.util.Set; 45 import javax.security.auth.x500.X500Principal; 46 import org.apache.harmony.security.utils.AlgNameMapper; 47 import org.apache.harmony.security.x509.Certificate; 48 import org.apache.harmony.security.x509.Extension; 49 import org.apache.harmony.security.x509.Extensions; 50 import org.apache.harmony.security.x509.TBSCertificate; 51 import org.apache.harmony.xnet.provider.jsse.OpenSSLProvider; 52 53 /** 54 * This class is an implementation of X509Certificate. It wraps 55 * the instance of org.apache.harmony.security.x509.Certificate 56 * built on the base of provided ASN.1 DER encoded form of 57 * Certificate structure (as specified in RFC 3280 58 * http://www.ietf.org/rfc/rfc3280.txt). 59 * @see org.apache.harmony.security.x509.Certificate 60 * @see java.security.cert.X509Certificate 61 */ 62 public final class X509CertImpl extends X509Certificate { 63 64 /** @serial */ 65 private static final long serialVersionUID = 2972248729446736154L; 66 67 /** the core object to be wrapped in X509Certificate */ 68 private final Certificate certificate; 69 70 // to speed up access to the info, the following fields 71 // cache values retrieved from the certificate object, 72 // initialized using the "single-check idiom". 73 private final TBSCertificate tbsCert; 74 private final Extensions extensions; 75 private volatile long notBefore = -1; 76 private volatile long notAfter = -1; 77 private volatile BigInteger serialNumber; 78 private volatile X500Principal issuer; 79 private volatile X500Principal subject; 80 private volatile byte[] tbsCertificate; 81 private volatile byte[] signature; 82 private volatile String sigAlgName; 83 private volatile String sigAlgOID; 84 private volatile byte[] sigAlgParams; 85 // indicates whether the signature algorithm parameters are null 86 private volatile boolean nullSigAlgParams; 87 private volatile PublicKey publicKey; 88 89 // encoding of the certificate 90 private volatile byte[] encoding; 91 92 /** 93 * Constructs the instance on the base of ASN.1 encoded 94 * form of X.509 certificate provided via stream parameter. 95 * @param in input stream containing ASN.1 encoded form of certificate. 96 * @throws CertificateException if some decoding problems occur. 97 */ 98 public X509CertImpl(InputStream in) throws CertificateException { 99 try { 100 // decode the Certificate object 101 this.certificate = (Certificate) Certificate.ASN1.decode(in); 102 // cache the values of TBSCertificate and Extensions 103 this.tbsCert = certificate.getTbsCertificate(); 104 this.extensions = tbsCert.getExtensions(); 105 } catch (IOException e) { 106 throw new CertificateException(e); 107 } 108 } 109 110 /** 111 * Constructs the instance on the base of existing Certificate object to 112 * be wrapped. 113 */ 114 public X509CertImpl(Certificate certificate) { 115 this.certificate = certificate; 116 // cache the values of TBSCertificate and Extensions 117 this.tbsCert = certificate.getTbsCertificate(); 118 this.extensions = tbsCert.getExtensions(); 119 } 120 121 /** 122 * Constructs the instance on the base of ASN.1 encoded 123 * form of X.509 certificate provided via array of bytes. 124 * @param encoding byte array containing ASN.1 encoded form of certificate. 125 * @throws IOException if some decoding problems occur. 126 */ 127 public X509CertImpl(byte[] encoding) throws IOException { 128 this((Certificate) Certificate.ASN1.decode(encoding)); 129 } 130 131 public void checkValidity() 132 throws CertificateExpiredException, CertificateNotYetValidException { 133 checkValidity(System.currentTimeMillis()); 134 } 135 136 public void checkValidity(Date date) 137 throws CertificateExpiredException, CertificateNotYetValidException { 138 checkValidity(date.getTime()); 139 } 140 141 private void checkValidity(long time) 142 throws CertificateExpiredException, CertificateNotYetValidException { 143 if (time < getNotBeforeInternal()) { 144 throw new CertificateNotYetValidException("current time: " + new Date(time) 145 + ", validation time: " + new Date(getNotBeforeInternal())); 146 } 147 if (time > getNotAfterInternal()) { 148 throw new CertificateExpiredException("current time: " + new Date(time) 149 + ", expiration time: " + new Date(getNotAfterInternal())); 150 } 151 } 152 153 public int getVersion() { 154 return tbsCert.getVersion() + 1; 155 } 156 157 public BigInteger getSerialNumber() { 158 BigInteger result = serialNumber; 159 if (result == null) { 160 serialNumber = result = tbsCert.getSerialNumber(); 161 } 162 return result; 163 } 164 165 public Principal getIssuerDN() { 166 return getIssuerX500Principal(); 167 } 168 169 public X500Principal getIssuerX500Principal() { 170 X500Principal result = issuer; 171 if (result == null) { 172 // retrieve the issuer's principal 173 issuer = result = tbsCert.getIssuer().getX500Principal(); 174 } 175 return result; 176 } 177 178 public Principal getSubjectDN() { 179 return getSubjectX500Principal(); 180 } 181 182 public X500Principal getSubjectX500Principal() { 183 X500Principal result = subject; 184 if (result == null) { 185 // retrieve the subject's principal 186 subject = result = tbsCert.getSubject().getX500Principal(); 187 } 188 return result; 189 } 190 191 public Date getNotBefore() { 192 return new Date(getNotBeforeInternal()); 193 } 194 195 private long getNotBeforeInternal() { 196 long result = notBefore; 197 if (result == -1) { 198 notBefore = result = tbsCert.getValidity().getNotBefore().getTime(); 199 } 200 return result; 201 } 202 203 public Date getNotAfter() { 204 return new Date(getNotAfterInternal()); 205 } 206 207 private long getNotAfterInternal() { 208 long result = notAfter; 209 if (result == -1) { 210 notAfter = result = tbsCert.getValidity().getNotAfter().getTime(); 211 } 212 return result; 213 } 214 215 public byte[] getTBSCertificate() throws CertificateEncodingException { 216 return getTbsCertificateInternal().clone(); 217 } 218 219 private byte[] getTbsCertificateInternal() { 220 byte[] result = tbsCertificate; 221 if (result == null) { 222 tbsCertificate = result = tbsCert.getEncoded(); 223 } 224 return result; 225 } 226 227 public byte[] getSignature() { 228 return getSignatureInternal().clone(); 229 } 230 231 private byte[] getSignatureInternal() { 232 byte[] result = signature; 233 if (result == null) { 234 signature = result = certificate.getSignatureValue(); 235 } 236 return result; 237 } 238 239 public String getSigAlgName() { 240 String result = sigAlgName; 241 if (result == null) { 242 String sigAlgOIDLocal = getSigAlgOID(); 243 // retrieve the name of the signing algorithm 244 result = AlgNameMapper.map2AlgName(sigAlgOIDLocal); 245 if (result == null) { 246 // if could not be found, use OID as a name 247 result = sigAlgOIDLocal; 248 } 249 sigAlgName = result; 250 } 251 return result; 252 } 253 254 public String getSigAlgOID() { 255 String result = sigAlgOID; 256 if (result == null) { 257 // if info was not retrieved (and cached), do it: 258 sigAlgOID = result = tbsCert.getSignature().getAlgorithm(); 259 } 260 return result; 261 } 262 263 public byte[] getSigAlgParams() { 264 if (nullSigAlgParams) { 265 return null; 266 } 267 byte[] result = sigAlgParams; 268 if (result == null) { 269 result = tbsCert.getSignature().getParameters(); 270 if (result == null) { 271 nullSigAlgParams = true; 272 return null; 273 } 274 sigAlgParams = result; 275 } 276 return result; 277 } 278 279 public boolean[] getIssuerUniqueID() { 280 return tbsCert.getIssuerUniqueID(); 281 } 282 283 public boolean[] getSubjectUniqueID() { 284 return tbsCert.getSubjectUniqueID(); 285 } 286 287 public boolean[] getKeyUsage() { 288 if (extensions == null) { 289 return null; 290 } 291 return extensions.valueOfKeyUsage(); 292 } 293 294 public List<String> getExtendedKeyUsage() 295 throws CertificateParsingException { 296 if (extensions == null) { 297 return null; 298 } 299 try { 300 return extensions.valueOfExtendedKeyUsage(); 301 } catch (IOException e) { 302 throw new CertificateParsingException(e); 303 } 304 } 305 306 public int getBasicConstraints() { 307 if (extensions == null) { 308 return Integer.MAX_VALUE; 309 } 310 return extensions.valueOfBasicConstrains(); 311 } 312 313 public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException { 314 if (extensions == null) { 315 return null; 316 } 317 try { 318 // Retrieve the extension value from the cached extensions object 319 // This extension is not checked for correctness during 320 // certificate generation, so now it can throw exception 321 return extensions.valueOfSubjectAlternativeName(); 322 } catch (IOException e) { 323 throw new CertificateParsingException(e); 324 } 325 } 326 327 /** 328 * @see java.security.cert.X509Certificate#getIssuerAlternativeNames() 329 * method documentation for more information. 330 */ 331 public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException { 332 if (extensions == null) { 333 return null; 334 } 335 try { 336 // Retrieve the extension value from the cached extensions object 337 // This extension is not checked for correctness during 338 // certificate generation, so now it can throw exception 339 return extensions.valueOfIssuerAlternativeName(); 340 } catch (IOException e) { 341 throw new CertificateParsingException(e); 342 } 343 } 344 345 @Override public byte[] getEncoded() throws CertificateEncodingException { 346 return getEncodedInternal().clone(); 347 } 348 private byte[] getEncodedInternal() throws CertificateEncodingException { 349 byte[] result = encoding; 350 if (encoding == null) { 351 encoding = result = certificate.getEncoded(); 352 } 353 return result; 354 } 355 356 @Override public PublicKey getPublicKey() { 357 PublicKey result = publicKey; 358 if (result == null) { 359 publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey(); 360 } 361 return result; 362 } 363 364 @Override public String toString() { 365 return certificate.toString(); 366 } 367 368 @Override public void verify(PublicKey key) 369 throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, 370 NoSuchProviderException, SignatureException { 371 372 Signature signature; 373 try { 374 signature = Signature.getInstance(getSigAlgName(), OpenSSLProvider.PROVIDER_NAME); 375 } catch (NoSuchAlgorithmException ignored) { 376 signature = Signature.getInstance(getSigAlgName()); 377 } 378 signature.initVerify(key); 379 // retrieve the encoding of the TBSCertificate structure 380 byte[] tbsCertificateLocal = getTbsCertificateInternal(); 381 // compute and verify the signature 382 signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length); 383 if (!signature.verify(certificate.getSignatureValue())) { 384 throw new SignatureException("Signature was not verified"); 385 } 386 } 387 388 @Override public void verify(PublicKey key, String sigProvider) 389 throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, 390 NoSuchProviderException, SignatureException { 391 392 Signature signature; 393 try { 394 if (sigProvider == null) { 395 signature = Signature.getInstance(getSigAlgName(), OpenSSLProvider.PROVIDER_NAME); 396 } else { 397 signature = Signature.getInstance(getSigAlgName(), sigProvider); 398 } 399 } catch (NoSuchAlgorithmException ignored) { 400 signature = Signature.getInstance(getSigAlgName(), sigProvider); 401 } 402 signature.initVerify(key); 403 // retrieve the encoding of the TBSCertificate structure 404 byte[] tbsCertificateLocal = getTbsCertificateInternal(); 405 // compute and verify the signature 406 signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length); 407 if (!signature.verify(certificate.getSignatureValue())) { 408 throw new SignatureException("Signature was not verified"); 409 } 410 } 411 412 @Override public Set<String> getNonCriticalExtensionOIDs() { 413 if (extensions == null) { 414 return null; 415 } 416 // retrieve the info from the cached extensions object 417 return extensions.getNonCriticalExtensions(); 418 } 419 420 @Override public Set<String> getCriticalExtensionOIDs() { 421 if (extensions == null) { 422 return null; 423 } 424 // retrieve the info from the cached extensions object 425 return extensions.getCriticalExtensions(); 426 } 427 428 @Override public byte[] getExtensionValue(String oid) { 429 if (extensions == null) { 430 return null; 431 } 432 // retrieve the info from the cached extensions object 433 Extension ext = extensions.getExtensionByOID(oid); 434 return (ext == null) ? null : ext.getRawExtnValue(); 435 } 436 437 @Override public boolean hasUnsupportedCriticalExtension() { 438 if (extensions == null) { 439 return false; 440 } 441 // retrieve the info from the cached extensions object 442 return extensions.hasUnsupportedCritical(); 443 } 444 445 } 446