blob: 5870f604459fc1a1e67f42a80026418a5a72f66e [file] [log] [blame]
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08001/*
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
23package org.apache.harmony.security.provider.cert;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.math.BigInteger;
28import java.security.InvalidKeyException;
29import java.security.NoSuchAlgorithmException;
30import java.security.NoSuchProviderException;
31import java.security.Principal;
32import java.security.PublicKey;
33import java.security.Signature;
34import java.security.SignatureException;
35import java.security.cert.CertificateEncodingException;
36import java.security.cert.CertificateException;
37import java.security.cert.CertificateExpiredException;
38import java.security.cert.CertificateNotYetValidException;
39import java.security.cert.CertificateParsingException;
40import java.security.cert.X509Certificate;
Brian Carlstrom12cd1f02010-06-22 23:43:20 -070041import java.security.interfaces.RSAPublicKey;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080042import java.util.Collection;
43import java.util.Date;
44import java.util.List;
45import java.util.Set;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080046import javax.security.auth.x500.X500Principal;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080047import org.apache.harmony.security.utils.AlgNameMapper;
48import org.apache.harmony.security.x509.Certificate;
49import org.apache.harmony.security.x509.Extension;
50import org.apache.harmony.security.x509.Extensions;
51import org.apache.harmony.security.x509.TBSCertificate;
Brian Carlstrom3e24c532010-05-06 00:23:21 -070052import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080053
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 */
63public class X509CertImpl extends X509Certificate {
64
65 /**
66 * @serial
67 */
68 private static final long serialVersionUID = 2972248729446736154L;
69
70 // the core object to be wrapped in X509Certificate
71 private final Certificate certificate;
72
73 // to speed up access to the info, the following fields
74 // cache values retrieved from the certificate object
75 private final TBSCertificate tbsCert;
76 private final Extensions extensions;
77 private long notBefore = -1;
78 private long notAfter;
79 private BigInteger serialNumber;
80 private X500Principal issuer;
81 private X500Principal subject;
82 private byte[] tbsCertificate;
83 private byte[] signature;
84 private String sigAlgName;
85 private String sigAlgOID;
86 private byte[] sigAlgParams;
87 // indicates whether the signature algorithm parameters are null
88 private boolean nullSigAlgParams;
89 private PublicKey publicKey;
90
91 // encoding of the certificate
Bob Lee5bd24292009-08-25 22:32:37 -070092// BEGIN android-changed
93 private volatile byte[] encoding;
94// END android-changed
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080095
96 //
97 // ---------------------- Constructors -------------------------------
98 //
99
100 /**
101 * Constructs the instance on the base of ASN.1 encoded
102 * form of X.509 certificate provided via stream parameter.
103 * @param in input stream containing ASN.1 encoded form of certificate.
104 * @throws CertificateException if some decoding problems occur.
105 */
106 public X509CertImpl(InputStream in) throws CertificateException {
107 try {
108 // decode the Certificate object
109 this.certificate = (Certificate) Certificate.ASN1.decode(in);
110 // cache the values of TBSCertificate and Extensions
111 this.tbsCert = certificate.getTbsCertificate();
112 this.extensions = tbsCert.getExtensions();
113 } catch (IOException e) {
114 throw new CertificateException(e);
115 }
116 }
117
118 /**
119 * Constructs the instance on the base of existing Certificate object to
120 * be wrapped.
121 */
122 public X509CertImpl(Certificate certificate) {
123 this.certificate = certificate;
124 // cache the values of TBSCertificate and Extensions
125 this.tbsCert = certificate.getTbsCertificate();
126 this.extensions = tbsCert.getExtensions();
127 }
128
129 /**
130 * Constructs the instance on the base of ASN.1 encoded
131 * form of X.509 certificate provided via array of bytes.
132 * @param encoding byte array containing ASN.1 encoded form of certificate.
133 * @throws IOException if some decoding problems occur.
134 */
135 public X509CertImpl(byte[] encoding) throws IOException {
136 this((Certificate) Certificate.ASN1.decode(encoding));
137 }
138
139 //
140 // ----------------- Public methods implementations ------------------
141 //
142
143 /**
144 * @see java.security.cert.X509Certificate#checkValidity()
145 * method documentation for more information.
146 */
147 public void checkValidity() throws CertificateExpiredException,
148 CertificateNotYetValidException {
149 if (notBefore == -1) {
150 // retrieve and cache the value of validity period
151 notBefore = tbsCert.getValidity().getNotBefore().getTime();
152 notAfter = tbsCert.getValidity().getNotAfter().getTime();
153 }
154 long time = System.currentTimeMillis();
155 if (time < notBefore) {
156 throw new CertificateNotYetValidException();
157 }
158 if (time > notAfter) {
159 throw new CertificateExpiredException();
160 }
161 }
162
163 /**
164 * @see java.security.cert.X509Certificate#checkValidity(Date)
165 * method documentation for more information.
166 */
167 public void checkValidity(Date date)
168 throws CertificateExpiredException,
169 CertificateNotYetValidException {
170 if (notBefore == -1) {
171 // retrieve and cache the value of validity period
172 notBefore = tbsCert.getValidity().getNotBefore().getTime();
173 notAfter = tbsCert.getValidity().getNotAfter().getTime();
174 }
175 long time = date.getTime();
176 if (time < notBefore) {
177 // BEGIN android-changed
178 throw new CertificateNotYetValidException("current time: " + date
179 + ", validation time: " + new Date(notBefore));
180 // END android-changed
181 }
182 if (time > notAfter) {
183 // BEGIN android-changed
184 throw new CertificateExpiredException("current time: " + date
185 + ", expiration time: " + new Date(notAfter));
186 // END android-changed
187 }
188 }
189
190 /**
191 * @see java.security.cert.X509Certificate#getVersion()
192 * method documentation for more information.
193 */
194 public int getVersion() {
195 return tbsCert.getVersion() + 1;
196 }
197
198 /**
199 * @see java.security.cert.X509Certificate#getSerialNumber()
200 * method documentation for more information.
201 */
202 public BigInteger getSerialNumber() {
203 if (serialNumber == null) {
204 serialNumber = tbsCert.getSerialNumber();
205 }
206 return serialNumber;
207 }
208
209 /**
210 * @see java.security.cert.X509Certificate#getIssuerDN()
211 * method documentation for more information.
212 */
213 public Principal getIssuerDN() {
214 if (issuer == null) {
215 // retrieve the issuer's principal
216 issuer = tbsCert.getIssuer().getX500Principal();
217 }
218 return issuer;
219 }
220
221 /**
222 * @see java.security.cert.X509Certificate#getIssuerX500Principal()
223 * method documentation for more information.
224 */
225 public X500Principal getIssuerX500Principal() {
226 if (issuer == null) {
227 // retrieve the issuer's principal
228 issuer = tbsCert.getIssuer().getX500Principal();
229 }
230 return issuer;
231 }
232
233 /**
234 * @see java.security.cert.X509Certificate#getSubjectDN()
235 * method documentation for more information.
236 */
237 public Principal getSubjectDN() {
238 if (subject == null) {
239 // retrieve the subject's principal
240 subject = tbsCert.getSubject().getX500Principal();
241 }
242 return subject;
243 }
244
245 /**
246 * @see java.security.cert.X509Certificate#getSubjectX500Principal()
247 * method documentation for more information.
248 */
249 public X500Principal getSubjectX500Principal() {
250 if (subject == null) {
251 // retrieve the subject's principal
252 subject = tbsCert.getSubject().getX500Principal();
253 }
254 return subject;
255 }
256
257 /**
258 * @see java.security.cert.X509Certificate#getNotBefore()
259 * method documentation for more information.
260 */
261 public Date getNotBefore() {
262 if (notBefore == -1) {
263 // the value was not retrieved from the certificate, do it:
264 notBefore = tbsCert.getValidity().getNotBefore().getTime();
265 notAfter = tbsCert.getValidity().getNotAfter().getTime();
266 }
267 return new Date(notBefore);
268 }
269
270 /**
271 * @see java.security.cert.X509Certificate#getNotAfter()
272 * method documentation for more information.
273 */
274 public Date getNotAfter() {
275 if (notBefore == -1) {
276 // the value was not retrieved from the certificate, do it:
277 notBefore = tbsCert.getValidity().getNotBefore().getTime();
278 notAfter = tbsCert.getValidity().getNotAfter().getTime();
279 }
280 return new Date(notAfter);
281 }
282
283 /**
284 * @see java.security.cert.X509Certificate#getTBSCertificate()
285 * method documentation for more information.
286 */
287 public byte[] getTBSCertificate()
288 throws CertificateEncodingException {
289 if (tbsCertificate == null) {
290 // retrieve the encoded form of the TBSCertificate structure
291 tbsCertificate = tbsCert.getEncoded();
292 }
293 byte[] result = new byte[tbsCertificate.length];
294 System.arraycopy(tbsCertificate, 0, result, 0, tbsCertificate.length);
295 return result;
296 }
297
298 /**
299 * @see java.security.cert.X509Certificate#getSignature()
300 * method documentation for more information.
301 */
302 public byte[] getSignature() {
303 if (signature == null) {
304 // retrieve the value of the signature
305 signature = certificate.getSignatureValue();
306 }
307 byte[] result = new byte[signature.length];
308 System.arraycopy(signature, 0, result, 0, signature.length);
309 return result;
310 }
311
312 /**
313 * @see java.security.cert.X509Certificate#getSigAlgName()
314 * method documentation for more information.
315 */
316 public String getSigAlgName() {
317 if (sigAlgOID == null) {
318 // if info was not retrieved (and cached), do it:
319 sigAlgOID = tbsCert.getSignature().getAlgorithm();
320 // retrieve the name of the signing algorithm
321 sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
322 if (sigAlgName == null) {
323 // if could not be found, use OID as a name
324 sigAlgName = sigAlgOID;
325 }
326 }
327 return sigAlgName;
328 }
329
330 /**
331 * @see java.security.cert.X509Certificate#getSigAlgOID()
332 * method documentation for more information.
333 */
334 public String getSigAlgOID() {
335 if (sigAlgOID == null) {
336 // if info was not retrieved (and cached), do it:
337 sigAlgOID = tbsCert.getSignature().getAlgorithm();
338 // retrieve the name of the signing algorithm
339 sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
340 if (sigAlgName == null) {
341 // if could not be found, use OID as a name
342 sigAlgName = sigAlgOID;
343 }
344 }
345 return sigAlgOID;
346 }
347
348 /**
349 * @see java.security.cert.X509Certificate#getSigAlgParams()
350 * method documentation for more information.
351 */
352 public byte[] getSigAlgParams() {
353 if (nullSigAlgParams) {
354 return null;
355 }
356 if (sigAlgParams == null) {
357 sigAlgParams = tbsCert.getSignature().getParameters();
358 if (sigAlgParams == null) {
359 nullSigAlgParams = true;
360 return null;
361 }
362 }
363 return sigAlgParams;
364 }
365
366 /**
367 * @see java.security.cert.X509Certificate#getIssuerUniqueID()
368 * method documentation for more information.
369 */
370 public boolean[] getIssuerUniqueID() {
371 return tbsCert.getIssuerUniqueID();
372 }
373
374 /**
375 * @see java.security.cert.X509Certificate#getSubjectUniqueID()
376 * method documentation for more information.
377 */
378 public boolean[] getSubjectUniqueID() {
379 return tbsCert.getSubjectUniqueID();
380 }
381
382 /**
383 * @see java.security.cert.X509Certificate#getKeyUsage()
384 * method documentation for more information.
385 */
386 public boolean[] getKeyUsage() {
387 if (extensions == null) {
388 return null;
389 }
390 return extensions.valueOfKeyUsage();
391 }
392
393 /**
394 * @see java.security.cert.X509Certificate#getExtendedKeyUsage()
395 * method documentation for more information.
396 */
397 public List/*<String>*/ getExtendedKeyUsage()
398 throws CertificateParsingException {
399 if (extensions == null) {
400 return null;
401 }
402 try {
403 return extensions.valueOfExtendedKeyUsage();
404 } catch (IOException e) {
405 throw new CertificateParsingException(e);
406 }
407 }
408
409 /**
410 * @see java.security.cert.X509Certificate#getBasicConstraints()
411 * method documentation for more information.
412 */
413 public int getBasicConstraints() {
414 if (extensions == null) {
415 return Integer.MAX_VALUE;
416 }
417 return extensions.valueOfBasicConstrains();
418 }
419
420 /**
421 * @see java.security.cert.X509Certificate#getSubjectAlternativeNames()
422 * method documentation for more information.
423 */
424 public Collection/*<List<?>>*/ getSubjectAlternativeNames()
425 throws CertificateParsingException {
426 if (extensions == null) {
427 return null;
428 }
429 try {
430 // Retrieve the extension value from the cached extensions object
431 // This extension is not checked for correctness during
432 // certificate generation, so now it can throw exception
433 return extensions.valueOfSubjectAlternativeName();
434 } catch (IOException e) {
435 throw new CertificateParsingException(e);
436 }
437 }
438
439 /**
440 * @see java.security.cert.X509Certificate#getIssuerAlternativeNames()
441 * method documentation for more information.
442 */
443 public Collection/*FIXME <List<?>>*/ getIssuerAlternativeNames()
444 throws CertificateParsingException {
445 if (extensions == null) {
446 return null;
447 }
448 try {
449 // Retrieve the extension value from the cached extensions object
450 // This extension is not checked for correctness during
451 // certificate generation, so now it can throw exception
452 return extensions.valueOfIssuerAlternativeName();
453 } catch (IOException e) {
454 throw new CertificateParsingException(e);
455 }
456 }
457
458 //
459 // ----- java.security.cert.Certificate methods implementations ------
460 //
461
462 /**
463 * @see java.security.cert.Certificate#getEncoded()
464 * method documentation for more information.
465 */
466 public byte[] getEncoded() throws CertificateEncodingException {
467 if (encoding == null) {
468 encoding = certificate.getEncoded();
469 }
470 byte[] result = new byte[encoding.length];
471 System.arraycopy(encoding, 0, result, 0, encoding.length);
472 return result;
473 }
474
475 /**
476 * @see java.security.cert.Certificate#getPublicKey()
477 * method documentation for more information.
478 */
479 public PublicKey getPublicKey() {
480 if (publicKey == null) {
481 // retrieve the public key from SubjectPublicKeyInfo
482 // substructure of X.509 certificate
483 publicKey = tbsCert.getSubjectPublicKeyInfo().getPublicKey();
484 }
485 return publicKey;
486 }
487
488 /**
489 * @see java.security.cert.Certificate#toString()
490 * method documentation for more information.
491 */
492 public String toString() {
493 return certificate.toString();
494 }
495
496 /**
497 * Verifies the signature of the certificate.
498 * @see java.security.cert.Certificate#verify(PublicKey)
499 * method documentation for more information.
500 */
501 public void verify(PublicKey key)
502 throws CertificateException, NoSuchAlgorithmException,
503 InvalidKeyException, NoSuchProviderException,
504 SignatureException {
505
506 // BEGIN android-added
507 if (getSigAlgName().endsWith("withRSA")) {
508 fastVerify(key);
509 return;
510 }
511 // END android-added
512
513 Signature signature = Signature.getInstance(getSigAlgName());
514 signature.initVerify(key);
515 // retrieve the encoding of the TBSCertificate structure
516 if (tbsCertificate == null) {
517 tbsCertificate = tbsCert.getEncoded();
518 }
519 // compute and verify the signature
520 signature.update(tbsCertificate, 0, tbsCertificate.length);
521 if (!signature.verify(certificate.getSignatureValue())) {
Elliott Hughes897538a2010-05-28 20:00:47 -0700522 throw new SignatureException("Signature was not verified");
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800523 }
524 }
525
526 /**
527 * Verifies the signature of the certificate.
528 * @see java.security.cert.Certificate#verify(PublicKey,String)
529 * method documentation for more information.
530 */
531 public void verify(PublicKey key, String sigProvider)
532 throws CertificateException, NoSuchAlgorithmException,
533 InvalidKeyException, NoSuchProviderException,
534 SignatureException {
Elliott Hughesf33eae72010-05-13 12:36:25 -0700535
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800536 // BEGIN android-added
537 if (getSigAlgName().endsWith("withRSA")) {
538 fastVerify(key);
539 return;
540 }
541 // END android-added
Elliott Hughesf33eae72010-05-13 12:36:25 -0700542
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800543 Signature signature =
544 Signature.getInstance(getSigAlgName(), sigProvider);
545 signature.initVerify(key);
546 // retrieve the encoding of the TBSCertificate structure
547 if (tbsCertificate == null) {
548 tbsCertificate = tbsCert.getEncoded();
549 }
550 // compute and verify the signature
551 signature.update(tbsCertificate, 0, tbsCertificate.length);
552 if (!signature.verify(certificate.getSignatureValue())) {
Elliott Hughes897538a2010-05-28 20:00:47 -0700553 throw new SignatureException("Signature was not verified");
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800554 }
555 }
556
557 // BEGIN android-added
558 /**
559 * Implements a faster RSA verification method that delegates to OpenSSL
560 * native code. In all other aspects it behaves just like the ordinary
561 * {@link verify} method.
Elliott Hughesf33eae72010-05-13 12:36:25 -0700562 *
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800563 * @param key The RSA public key to use
Elliott Hughesf33eae72010-05-13 12:36:25 -0700564 *
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800565 * @throws SignatureException If the verification fails.
Elliott Hughesf33eae72010-05-13 12:36:25 -0700566 * @throws InvalidKeyException
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800567 */
568 private void fastVerify(PublicKey key) throws SignatureException,
Urs Grob87eb4de2009-07-22 20:06:03 +0200569 InvalidKeyException, NoSuchAlgorithmException {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800570 if (!(key instanceof RSAPublicKey)) {
Elliott Hughes897538a2010-05-28 20:00:47 -0700571 throw new InvalidKeyException("key is not an instance of RSAPublicKey");
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800572 }
573 RSAPublicKey rsaKey = (RSAPublicKey) key;
Elliott Hughesf33eae72010-05-13 12:36:25 -0700574
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800575 String algorithm = getSigAlgName();
Urs Grob87eb4de2009-07-22 20:06:03 +0200576
577 // We don't support MD2 anymore. This needs to also check for aliases
578 // and OIDs.
579 if ("MD2withRSA".equalsIgnoreCase(algorithm) ||
580 "MD2withRSAEncryption".equalsIgnoreCase(algorithm) ||
581 "1.2.840.113549.1.1.2".equalsIgnoreCase(algorithm) ||
582 "MD2/RSA".equalsIgnoreCase(algorithm)) {
583 throw new NoSuchAlgorithmException(algorithm);
584 }
585
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800586 int i = algorithm.indexOf("with");
587 algorithm = algorithm.substring(i + 4) + "-" + algorithm.substring(0, i);
Elliott Hughesf33eae72010-05-13 12:36:25 -0700588
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800589 if (tbsCertificate == null) {
590 tbsCertificate = tbsCert.getEncoded();
591 }
592
593 byte[] sig = certificate.getSignatureValue();
Brian Carlstrom3e24c532010-05-06 00:23:21 -0700594 if (!NativeCrypto.verifySignature(tbsCertificate, sig, algorithm, rsaKey)) {
Elliott Hughes897538a2010-05-28 20:00:47 -0700595 throw new SignatureException("Signature was not verified");
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800596 }
597 }
598 // END android-added
599
600 //
601 // ----- java.security.cert.X509Extension methods implementations ----
602 //
603
604 /**
605 * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs()
606 * method documentation for more information.
607 */
608 public Set getNonCriticalExtensionOIDs() {
609 if (extensions == null) {
610 return null;
611 }
612 // retrieve the info from the cached extensions object
613 return extensions.getNonCriticalExtensions();
614 }
615
616 /**
617 * @see java.security.cert.X509Extension#getCriticalExtensionOIDs()
618 * method documentation for more information.
619 */
620 public Set getCriticalExtensionOIDs() {
621 if (extensions == null) {
622 return null;
623 }
624 // retrieve the info from the cached extensions object
625 return extensions.getCriticalExtensions();
626 }
627
628 /**
629 * @see java.security.cert.X509Extension#getExtensionValue(String)
630 * method documentation for more information.
631 */
632 public byte[] getExtensionValue(String oid) {
633 if (extensions == null) {
634 return null;
635 }
636 // retrieve the info from the cached extensions object
637 Extension ext = extensions.getExtensionByOID(oid);
638 return (ext == null) ? null : ext.getRawExtnValue();
639 }
640
641 /**
642 * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension()
643 * method documentation for more information.
644 */
645 public boolean hasUnsupportedCriticalExtension() {
646 if (extensions == null) {
647 return false;
648 }
649 // retrieve the info from the cached extensions object
650 return extensions.hasUnsupportedCritical();
651 }
652
653}