1 /* X509CRL.java -- X.509 certificate revocation list. 2 Copyright (C) 2003, 2004 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.OID; 42 import gnu.java.security.der.BitString; 43 import gnu.java.security.der.DER; 44 import gnu.java.security.der.DERReader; 45 import gnu.java.security.der.DERValue; 46 import gnu.java.security.x509.ext.Extension; 47 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.math.BigInteger; 51 import java.security.InvalidKeyException; 52 import java.security.NoSuchAlgorithmException; 53 import java.security.NoSuchProviderException; 54 import java.security.Principal; 55 import java.security.PublicKey; 56 import java.security.Signature; 57 import java.security.SignatureException; 58 import java.security.cert.CRLException; 59 import java.security.cert.Certificate; 60 import java.util.Collection; 61 import java.util.Collections; 62 import java.util.Date; 63 import java.util.HashMap; 64 import java.util.HashSet; 65 import java.util.Iterator; 66 import java.util.Set; 67 68 import javax.security.auth.x500.X500Principal; 69 70 /** 71 * X.509 certificate revocation lists. 72 * 73 * @author Casey Marshall (rsdio@metastatic.org) 74 */ 75 public class X509CRL extends java.security.cert.X509CRL 76 implements GnuPKIExtension 77 { 78 79 // Constants and fields. 80 // ------------------------------------------------------------------------ 81 82 private static final boolean DEBUG = false; debug(String msg)83 private static void debug(String msg) 84 { 85 if (DEBUG) 86 { 87 System.err.print(">> X509CRL: "); 88 System.err.println(msg); 89 } 90 } 91 92 private static final OID ID_DSA = new OID("1.2.840.10040.4.1"); 93 private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3"); 94 private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1"); 95 private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2"); 96 private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4"); 97 private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5"); 98 99 private byte[] encoded; 100 101 private byte[] tbsCRLBytes; 102 private int version; 103 private OID algId; 104 private byte[] algParams; 105 private Date thisUpdate; 106 private Date nextUpdate; 107 private X500DistinguishedName issuerDN; 108 private HashMap revokedCerts; 109 private HashMap extensions; 110 111 private OID sigAlg; 112 private byte[] sigAlgParams; 113 private byte[] rawSig; 114 private byte[] signature; 115 116 // Constructors. 117 // ------------------------------------------------------------------------ 118 119 /** 120 * Create a new X.509 CRL. 121 * 122 * @param encoded The DER encoded CRL. 123 * @throws CRLException If the input bytes are incorrect. 124 * @throws IOException If the input bytes cannot be read. 125 */ X509CRL(InputStream encoded)126 public X509CRL(InputStream encoded) throws CRLException, IOException 127 { 128 super(); 129 revokedCerts = new HashMap(); 130 extensions = new HashMap(); 131 try 132 { 133 parse(encoded); 134 } 135 catch (IOException ioe) 136 { 137 ioe.printStackTrace(); 138 throw ioe; 139 } 140 catch (Exception x) 141 { 142 x.printStackTrace(); 143 throw new CRLException(x.toString()); 144 } 145 } 146 147 // X509CRL methods. 148 // ------------------------------------------------------------------------ 149 equals(Object o)150 public boolean equals(Object o) 151 { 152 if (!(o instanceof X509CRL)) 153 return false; 154 return ((X509CRL) o).getRevokedCertificates().equals(revokedCerts.values()); 155 } 156 hashCode()157 public int hashCode() 158 { 159 return revokedCerts.hashCode(); 160 } 161 getEncoded()162 public byte[] getEncoded() throws CRLException 163 { 164 return (byte[]) encoded.clone(); 165 } 166 verify(PublicKey key)167 public void verify(PublicKey key) 168 throws CRLException, NoSuchAlgorithmException, InvalidKeyException, 169 NoSuchProviderException, SignatureException 170 { 171 Signature sig = Signature.getInstance(sigAlg.toString()); 172 doVerify(sig, key); 173 } 174 verify(PublicKey key, String provider)175 public void verify(PublicKey key, String provider) 176 throws CRLException, NoSuchAlgorithmException, InvalidKeyException, 177 NoSuchProviderException, SignatureException 178 { 179 Signature sig = Signature.getInstance(sigAlg.toString(), provider); 180 doVerify(sig, key); 181 } 182 getVersion()183 public int getVersion() 184 { 185 return version; 186 } 187 getIssuerDN()188 public Principal getIssuerDN() 189 { 190 return issuerDN; 191 } 192 getIssuerX500Principal()193 public X500Principal getIssuerX500Principal() 194 { 195 return new X500Principal(issuerDN.getDer()); 196 } 197 getThisUpdate()198 public Date getThisUpdate() 199 { 200 return (Date) thisUpdate.clone(); 201 } 202 getNextUpdate()203 public Date getNextUpdate() 204 { 205 if (nextUpdate != null) 206 return (Date) nextUpdate.clone(); 207 return null; 208 } 209 getRevokedCertificate(BigInteger serialNo)210 public java.security.cert.X509CRLEntry getRevokedCertificate(BigInteger serialNo) 211 { 212 return (java.security.cert.X509CRLEntry) revokedCerts.get(serialNo); 213 } 214 getRevokedCertificates()215 public Set getRevokedCertificates() 216 { 217 return Collections.unmodifiableSet(new HashSet(revokedCerts.values())); 218 } 219 getTBSCertList()220 public byte[] getTBSCertList() throws CRLException 221 { 222 return (byte[]) tbsCRLBytes.clone(); 223 } 224 getSignature()225 public byte[] getSignature() 226 { 227 return (byte[]) rawSig.clone(); 228 } 229 getSigAlgName()230 public String getSigAlgName() 231 { 232 if (sigAlg.equals(ID_DSA_WITH_SHA1)) 233 return "SHA1withDSA"; 234 if (sigAlg.equals(ID_RSA_WITH_MD2)) 235 return "MD2withRSA"; 236 if (sigAlg.equals(ID_RSA_WITH_MD5)) 237 return "MD5withRSA"; 238 if (sigAlg.equals(ID_RSA_WITH_SHA1)) 239 return "SHA1withRSA"; 240 return "unknown"; 241 } 242 getSigAlgOID()243 public String getSigAlgOID() 244 { 245 return sigAlg.toString(); 246 } 247 getSigAlgParams()248 public byte[] getSigAlgParams() 249 { 250 if (sigAlgParams != null) 251 return (byte[]) sigAlgParams.clone(); 252 return null; 253 } 254 255 // X509Extension methods. 256 // ------------------------------------------------------------------------ 257 hasUnsupportedCriticalExtension()258 public boolean hasUnsupportedCriticalExtension() 259 { 260 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 261 { 262 Extension e = (Extension) it.next(); 263 if (e.isCritical() && !e.isSupported()) 264 return true; 265 } 266 return false; 267 } 268 getCriticalExtensionOIDs()269 public Set getCriticalExtensionOIDs() 270 { 271 HashSet s = new HashSet(); 272 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 273 { 274 Extension e = (Extension) it.next(); 275 if (e.isCritical()) 276 s.add(e.getOid().toString()); 277 } 278 return Collections.unmodifiableSet(s); 279 } 280 getNonCriticalExtensionOIDs()281 public Set getNonCriticalExtensionOIDs() 282 { 283 HashSet s = new HashSet(); 284 for (Iterator it = extensions.values().iterator(); it.hasNext(); ) 285 { 286 Extension e = (Extension) it.next(); 287 if (!e.isCritical()) 288 s.add(e.getOid().toString()); 289 } 290 return Collections.unmodifiableSet(s); 291 } 292 getExtensionValue(String oid)293 public byte[] getExtensionValue(String oid) 294 { 295 Extension e = getExtension(new OID(oid)); 296 if (e != null) 297 { 298 return e.getValue().getEncoded(); 299 } 300 return null; 301 } 302 303 // GnuPKIExtension method. 304 // ------------------------------------------------------------------------- 305 getExtension(OID oid)306 public Extension getExtension(OID oid) 307 { 308 return (Extension) extensions.get(oid); 309 } 310 getExtensions()311 public Collection getExtensions() 312 { 313 return extensions.values(); 314 } 315 316 // CRL methods. 317 // ------------------------------------------------------------------------- 318 toString()319 public String toString() 320 { 321 return X509CRL.class.getName(); 322 } 323 isRevoked(Certificate cert)324 public boolean isRevoked(Certificate cert) 325 { 326 if (!(cert instanceof java.security.cert.X509Certificate)) 327 throw new IllegalArgumentException("not a X.509 certificate"); 328 BigInteger certSerial = 329 ((java.security.cert.X509Certificate) cert).getSerialNumber(); 330 X509CRLEntry ent = (X509CRLEntry) revokedCerts.get(certSerial); 331 if (ent == null) 332 return false; 333 return ent.getRevocationDate().compareTo(new Date()) < 0; 334 } 335 336 // Own methods. 337 // ------------------------------------------------------------------------ 338 doVerify(Signature sig, PublicKey key)339 private void doVerify(Signature sig, PublicKey key) 340 throws CRLException, InvalidKeyException, SignatureException 341 { 342 sig.initVerify(key); 343 sig.update(tbsCRLBytes); 344 if (!sig.verify(signature)) 345 throw new CRLException("signature not verified"); 346 } 347 parse(InputStream in)348 private void parse(InputStream in) throws Exception 349 { 350 // CertificateList ::= SEQUENCE { 351 DERReader der = new DERReader(in); 352 DERValue val = der.read(); 353 debug("start CertificateList len == " + val.getLength()); 354 if (!val.isConstructed()) 355 throw new IOException("malformed CertificateList"); 356 encoded = val.getEncoded(); 357 358 // tbsCertList ::= SEQUENCE { -- TBSCertList 359 val = der.read(); 360 if (!val.isConstructed()) 361 throw new IOException("malformed TBSCertList"); 362 debug("start tbsCertList len == " + val.getLength()); 363 tbsCRLBytes = val.getEncoded(); 364 365 // version Version OPTIONAL, 366 // -- If present must be v2 367 val = der.read(); 368 if (val.getValue() instanceof BigInteger) 369 { 370 version = ((BigInteger) val.getValue()).intValue() + 1; 371 val = der.read(); 372 } 373 else 374 version = 1; 375 debug("read version == " + version); 376 377 // signature AlgorithmIdentifier, 378 debug("start AlgorithmIdentifier len == " + val.getLength()); 379 if (!val.isConstructed()) 380 throw new IOException("malformed AlgorithmIdentifier"); 381 DERValue algIdVal = der.read(); 382 algId = (OID) algIdVal.getValue(); 383 debug("read object identifier == " + algId); 384 if (val.getLength() > algIdVal.getEncodedLength()) 385 { 386 val = der.read(); 387 debug("read parameters len == " + val.getEncodedLength()); 388 algParams = val.getEncoded(); 389 if (val.isConstructed()) 390 in.skip(val.getLength()); 391 } 392 393 // issuer Name, 394 val = der.read(); 395 issuerDN = new X500DistinguishedName(val.getEncoded()); 396 der.skip(val.getLength()); 397 debug("read issuer == " + issuerDN); 398 399 // thisUpdate Time, 400 thisUpdate = (Date) der.read().getValue(); 401 debug("read thisUpdate == " + thisUpdate); 402 403 // nextUpdate Time OPTIONAL, 404 val = der.read(); 405 if (val.getValue() instanceof Date) 406 { 407 nextUpdate = (Date) val.getValue(); 408 debug("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 debug("start Extensions len == " + exts.getLength()); 437 int len = 0; 438 while (len < exts.getLength()) 439 { 440 DERValue ext = der.read(); 441 if (!ext.isConstructed()) 442 throw new IOException("malformed Extension"); 443 Extension e = new Extension(ext.getEncoded()); 444 extensions.put(e.getOid(), e); 445 der.skip(ext.getLength()); 446 len += ext.getEncodedLength(); 447 debug("current count == " + len); 448 } 449 val = der.read(); 450 } 451 452 debug("read tag == " + val.getTag()); 453 if (!val.isConstructed()) 454 throw new IOException("malformed AlgorithmIdentifier"); 455 debug("start AlgorithmIdentifier len == " + val.getLength()); 456 DERValue sigAlgVal = der.read(); 457 debug("read tag == " + sigAlgVal.getTag()); 458 if (sigAlgVal.getTag() != DER.OBJECT_IDENTIFIER) 459 throw new IOException("malformed AlgorithmIdentifier"); 460 sigAlg = (OID) sigAlgVal.getValue(); 461 debug("signature id == " + sigAlg); 462 debug("sigAlgVal length == " + sigAlgVal.getEncodedLength()); 463 if (val.getLength() > sigAlgVal.getEncodedLength()) 464 { 465 val = der.read(); 466 debug("sig params tag = " + val.getTag() + " len == " + val.getEncodedLength()); 467 sigAlgParams = (byte[]) val.getEncoded(); 468 if (val.isConstructed()) 469 in.skip(val.getLength()); 470 } 471 val = der.read(); 472 debug("read tag = " + val.getTag()); 473 rawSig = val.getEncoded(); 474 signature = ((BitString) val.getValue()).toByteArray(); 475 } 476 } 477