1 /* X509CRL.java -- X.509 certificate revocation list. 2 Copyright (C) 2003, 2004, 2010 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.java.security.x509; 40 41 import gnu.java.security.Configuration; 42 import gnu.java.security.OID; 43 import gnu.java.security.der.BitString; 44 import gnu.java.security.der.DER; 45 import gnu.java.security.der.DERReader; 46 import gnu.java.security.der.DERValue; 47 import gnu.java.security.x509.ext.Extension; 48 49 import java.io.IOException; 50 import java.io.InputStream; 51 import java.math.BigInteger; 52 import java.security.InvalidKeyException; 53 import java.security.NoSuchAlgorithmException; 54 import java.security.NoSuchProviderException; 55 import java.security.Principal; 56 import java.security.PublicKey; 57 import java.security.Signature; 58 import java.security.SignatureException; 59 import java.security.cert.CRLException; 60 import java.security.cert.Certificate; 61 import java.util.Collection; 62 import java.util.Collections; 63 import java.util.Date; 64 import java.util.HashMap; 65 import java.util.HashSet; 66 import java.util.Iterator; 67 import java.util.Set; 68 import java.util.logging.Logger; 69 70 import javax.security.auth.x500.X500Principal; 71 72 /** 73 * X.509 certificate revocation lists. 74 * 75 * @author Casey Marshall (rsdio@metastatic.org) 76 */ 77 public class X509CRL extends java.security.cert.X509CRL 78 implements GnuPKIExtension 79 { 80 private static final Logger log = Configuration.DEBUG ? 81 Logger.getLogger(X509CRL.class.getName()) : null; 82 83 private static final OID ID_DSA = new OID("1.2.840.10040.4.1"); 84 private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3"); 85 private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1"); 86 private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2"); 87 private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4"); 88 private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5"); 89 90 private byte[] encoded; 91 92 private byte[] tbsCRLBytes; 93 private int version; 94 private OID algId; 95 private byte[] algParams; 96 private Date thisUpdate; 97 private Date nextUpdate; 98 private X500DistinguishedName issuerDN; 99 private HashMap revokedCerts; 100 private HashMap extensions; 101 102 private OID sigAlg; 103 private byte[] sigAlgParams; 104 private byte[] rawSig; 105 private byte[] signature; 106 107 // Constructors. 108 // ------------------------------------------------------------------------ 109 110 /** 111 * Create a new X.509 CRL. 112 * 113 * @param encoded The DER encoded CRL. 114 * @throws CRLException If the input bytes are incorrect. 115 * @throws IOException If the input bytes cannot be read. 116 */ X509CRL(InputStream encoded)117 public X509CRL(InputStream encoded) throws CRLException, IOException 118 { 119 super(); 120 revokedCerts = new HashMap(); 121 extensions = new HashMap(); 122 try 123 { 124 parse(encoded); 125 } 126 catch (IOException ioe) 127 { 128 ioe.printStackTrace(); 129 throw ioe; 130 } 131 catch (Exception x) 132 { 133 x.printStackTrace(); 134 throw new CRLException(x.toString()); 135 } 136 } 137 138 // X509CRL methods. 139 // ------------------------------------------------------------------------ 140 equals(Object o)141 public boolean equals(Object o) 142 { 143 if (!(o instanceof X509CRL)) 144 return false; 145 return ((X509CRL) o).getRevokedCertificates().equals(revokedCerts.values()); 146 } 147 hashCode()148 public int hashCode() 149 { 150 return revokedCerts.hashCode(); 151 } 152 getEncoded()153 public byte[] getEncoded() throws CRLException 154 { 155 return (byte[]) encoded.clone(); 156 } 157 verify(PublicKey key)158 public void verify(PublicKey key) 159 throws CRLException, NoSuchAlgorithmException, InvalidKeyException, 160 NoSuchProviderException, SignatureException 161 { 162 Signature sig = Signature.getInstance(sigAlg.toString()); 163 doVerify(sig, key); 164 } 165 verify(PublicKey key, String provider)166 public void verify(PublicKey key, String provider) 167 throws CRLException, NoSuchAlgorithmException, InvalidKeyException, 168 NoSuchProviderException, SignatureException 169 { 170 Signature sig = Signature.getInstance(sigAlg.toString(), provider); 171 doVerify(sig, key); 172 } 173 getVersion()174 public int getVersion() 175 { 176 return version; 177 } 178 getIssuerDN()179 public Principal getIssuerDN() 180 { 181 return issuerDN; 182 } 183 getIssuerX500Principal()184 public X500Principal getIssuerX500Principal() 185 { 186 return new X500Principal(issuerDN.getDer()); 187 } 188 getThisUpdate()189 public Date getThisUpdate() 190 { 191 return (Date) thisUpdate.clone(); 192 } 193 getNextUpdate()194 public Date getNextUpdate() 195 { 196 if (nextUpdate != null) 197 return (Date) nextUpdate.clone(); 198 return null; 199 } 200 getRevokedCertificate(BigInteger serialNo)201 public java.security.cert.X509CRLEntry getRevokedCertificate(BigInteger serialNo) 202 { 203 return (java.security.cert.X509CRLEntry) revokedCerts.get(serialNo); 204 } 205 getRevokedCertificates()206 public Set getRevokedCertificates() 207 { 208 return Collections.unmodifiableSet(new HashSet(revokedCerts.values())); 209 } 210 getTBSCertList()211 public byte[] getTBSCertList() throws CRLException 212 { 213 return (byte[]) tbsCRLBytes.clone(); 214 } 215 getSignature()216 public byte[] getSignature() 217 { 218 return (byte[]) rawSig.clone(); 219 } 220 getSigAlgName()221 public String getSigAlgName() 222 { 223 if (sigAlg.equals(ID_DSA_WITH_SHA1)) 224 return "SHA1withDSA"; 225 if (sigAlg.equals(ID_RSA_WITH_MD2)) 226 return "MD2withRSA"; 227 if (sigAlg.equals(ID_RSA_WITH_MD5)) 228 return "MD5withRSA"; 229 if (sigAlg.equals(ID_RSA_WITH_SHA1)) 230 return "SHA1withRSA"; 231 return "unknown"; 232 } 233 getSigAlgOID()234 public String getSigAlgOID() 235 { 236 return sigAlg.toString(); 237 } 238 getSigAlgParams()239 public byte[] getSigAlgParams() 240 { 241 if (sigAlgParams != null) 242 return (byte[]) sigAlgParams.clone(); 243 return null; 244 } 245 246 // X509Extension methods. 247 // ------------------------------------------------------------------------ 248 hasUnsupportedCriticalExtension()249 public boolean hasUnsupportedCriticalExtension() 250 { 251 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 252 { 253 Extension e = (Extension) it.next(); 254 if (e.isCritical() && !e.isSupported()) 255 return true; 256 } 257 return false; 258 } 259 getCriticalExtensionOIDs()260 public Set getCriticalExtensionOIDs() 261 { 262 HashSet s = new HashSet(); 263 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 264 { 265 Extension e = (Extension) it.next(); 266 if (e.isCritical()) 267 s.add(e.getOid().toString()); 268 } 269 return Collections.unmodifiableSet(s); 270 } 271 getNonCriticalExtensionOIDs()272 public Set getNonCriticalExtensionOIDs() 273 { 274 HashSet s = new HashSet(); 275 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 276 { 277 Extension e = (Extension) it.next(); 278 if (!e.isCritical()) 279 s.add(e.getOid().toString()); 280 } 281 return Collections.unmodifiableSet(s); 282 } 283 getExtensionValue(String oid)284 public byte[] getExtensionValue(String oid) 285 { 286 Extension e = getExtension(new OID(oid)); 287 if (e != null) 288 { 289 return e.getValue().getEncoded(); 290 } 291 return null; 292 } 293 294 // GnuPKIExtension method. 295 // ------------------------------------------------------------------------- 296 getExtension(OID oid)297 public Extension getExtension(OID oid) 298 { 299 return (Extension) extensions.get(oid); 300 } 301 getExtensions()302 public Collection getExtensions() 303 { 304 return extensions.values(); 305 } 306 307 // CRL methods. 308 // ------------------------------------------------------------------------- 309 toString()310 public String toString() 311 { 312 return X509CRL.class.getName(); 313 } 314 isRevoked(Certificate cert)315 public boolean isRevoked(Certificate cert) 316 { 317 if (!(cert instanceof java.security.cert.X509Certificate)) 318 throw new IllegalArgumentException("not a X.509 certificate"); 319 BigInteger certSerial = 320 ((java.security.cert.X509Certificate) cert).getSerialNumber(); 321 X509CRLEntry ent = (X509CRLEntry) revokedCerts.get(certSerial); 322 if (ent == null) 323 return false; 324 return ent.getRevocationDate().compareTo(new Date()) < 0; 325 } 326 327 // Own methods. 328 // ------------------------------------------------------------------------ 329 doVerify(Signature sig, PublicKey key)330 private void doVerify(Signature sig, PublicKey key) 331 throws CRLException, InvalidKeyException, SignatureException 332 { 333 sig.initVerify(key); 334 sig.update(tbsCRLBytes); 335 if (!sig.verify(signature)) 336 throw new CRLException("signature not verified"); 337 } 338 parse(InputStream in)339 private void parse(InputStream in) throws Exception 340 { 341 // CertificateList ::= SEQUENCE { 342 DERReader der = new DERReader(in); 343 DERValue val = der.read(); 344 if (Configuration.DEBUG) 345 log.fine("start CertificateList len == " + val.getLength()); 346 if (!val.isConstructed()) 347 throw new IOException("malformed CertificateList"); 348 encoded = val.getEncoded(); 349 350 // tbsCertList ::= SEQUENCE { -- TBSCertList 351 val = der.read(); 352 if (!val.isConstructed()) 353 throw new IOException("malformed TBSCertList"); 354 if (Configuration.DEBUG) 355 log.fine("start tbsCertList len == " + val.getLength()); 356 tbsCRLBytes = val.getEncoded(); 357 358 // version Version OPTIONAL, 359 // -- If present must be v2 360 val = der.read(); 361 if (val.getValue() instanceof BigInteger) 362 { 363 version = ((BigInteger) val.getValue()).intValue() + 1; 364 val = der.read(); 365 } 366 else 367 version = 1; 368 if (Configuration.DEBUG) 369 log.fine("read version == " + version); 370 371 // signature AlgorithmIdentifier, 372 if (Configuration.DEBUG) 373 log.fine("start AlgorithmIdentifier len == " + val.getLength()); 374 if (!val.isConstructed()) 375 throw new IOException("malformed AlgorithmIdentifier"); 376 DERValue algIdVal = der.read(); 377 algId = (OID) algIdVal.getValue(); 378 if (Configuration.DEBUG) 379 log.fine("read object identifier == " + algId); 380 if (val.getLength() > algIdVal.getEncodedLength()) 381 { 382 val = der.read(); 383 if (Configuration.DEBUG) 384 log.fine("read parameters len == " + val.getEncodedLength()); 385 algParams = val.getEncoded(); 386 if (val.isConstructed()) 387 in.skip(val.getLength()); 388 } 389 390 // issuer Name, 391 val = der.read(); 392 issuerDN = new X500DistinguishedName(val.getEncoded()); 393 der.skip(val.getLength()); 394 if (Configuration.DEBUG) 395 log.fine("read issuer == " + issuerDN); 396 397 // thisUpdate Time, 398 thisUpdate = (Date) der.read().getValue(); 399 if (Configuration.DEBUG) 400 log.fine("read thisUpdate == " + thisUpdate); 401 402 // nextUpdate Time OPTIONAL, 403 val = der.read(); 404 if (val.getValue() instanceof Date) 405 { 406 nextUpdate = (Date) val.getValue(); 407 if (Configuration.DEBUG) 408 log.fine("read nextUpdate == " + nextUpdate); 409 val = der.read(); 410 } 411 412 // revokedCertificates SEQUENCE OF SEQUENCE { 413 // -- X509CRLEntry objects... 414 // } OPTIONAL, 415 if (val.getTag() != 0) 416 { 417 int len = 0; 418 while (len < val.getLength()) 419 { 420 X509CRLEntry entry = new X509CRLEntry(version, der); 421 revokedCerts.put(entry.getSerialNumber(), entry); 422 len += entry.getEncoded().length; 423 } 424 val = der.read(); 425 } 426 427 // crlExtensions [0] EXPLICIT Extensions OPTIONAL 428 // -- if present MUST be v2 429 if (val.getTagClass() != DER.UNIVERSAL && val.getTag() == 0) 430 { 431 if (version < 2) 432 throw new IOException("extra data in CRL"); 433 DERValue exts = der.read(); 434 if (!exts.isConstructed()) 435 throw new IOException("malformed Extensions"); 436 if (Configuration.DEBUG) 437 log.fine("start Extensions len == " + exts.getLength()); 438 int len = 0; 439 while (len < exts.getLength()) 440 { 441 DERValue ext = der.read(); 442 if (!ext.isConstructed()) 443 throw new IOException("malformed Extension"); 444 Extension e = new Extension(ext.getEncoded()); 445 extensions.put(e.getOid(), e); 446 der.skip(ext.getLength()); 447 len += ext.getEncodedLength(); 448 if (Configuration.DEBUG) 449 log.fine("current count == " + len); 450 } 451 val = der.read(); 452 } 453 454 if (Configuration.DEBUG) 455 log.fine("read tag == " + val.getTag()); 456 if (!val.isConstructed()) 457 throw new IOException("malformed AlgorithmIdentifier"); 458 if (Configuration.DEBUG) 459 log.fine("start AlgorithmIdentifier len == " + val.getLength()); 460 DERValue sigAlgVal = der.read(); 461 if (Configuration.DEBUG) 462 log.fine("read tag == " + sigAlgVal.getTag()); 463 if (sigAlgVal.getTag() != DER.OBJECT_IDENTIFIER) 464 throw new IOException("malformed AlgorithmIdentifier"); 465 sigAlg = (OID) sigAlgVal.getValue(); 466 if (Configuration.DEBUG) 467 { 468 log.fine("signature id == " + sigAlg); 469 log.fine("sigAlgVal length == " + sigAlgVal.getEncodedLength()); 470 } 471 if (val.getLength() > sigAlgVal.getEncodedLength()) 472 { 473 val = der.read(); 474 if (Configuration.DEBUG) 475 log.fine("sig params tag = " + val.getTag() + " len == " 476 + val.getEncodedLength()); 477 sigAlgParams = (byte[]) val.getEncoded(); 478 if (val.isConstructed()) 479 in.skip(val.getLength()); 480 } 481 val = der.read(); 482 if (Configuration.DEBUG) 483 log.fine("read tag = " + val.getTag()); 484 rawSig = val.getEncoded(); 485 signature = ((BitString) val.getValue()).toByteArray(); 486 } 487 } 488