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