Source for gnu.java.security.x509.X509CertPath

   1: /* X509CertPath.java -- an X.509 certificate path.
   2:    Copyright (C) 2004  Free Software Fonudation, 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.DER;
  43: import gnu.java.security.der.DEREncodingException;
  44: import gnu.java.security.der.DERReader;
  45: import gnu.java.security.der.DERValue;
  46: 
  47: import java.io.ByteArrayInputStream;
  48: import java.io.ByteArrayOutputStream;
  49: import java.io.IOException;
  50: import java.io.InputStream;
  51: import java.math.BigInteger;
  52: import java.security.cert.CertPath;
  53: import java.security.cert.Certificate;
  54: import java.security.cert.CertificateEncodingException;
  55: import java.security.cert.CertificateException;
  56: import java.util.ArrayList;
  57: import java.util.Arrays;
  58: import java.util.Collections;
  59: import java.util.Iterator;
  60: import java.util.LinkedList;
  61: import java.util.List;
  62: 
  63: /**
  64:  * A certificate path (or certificate chain) of X509Certificates.
  65:  *
  66:  * @author Casey Marshall (rsdio@metastatic.org)
  67:  */
  68: public class X509CertPath extends CertPath
  69: {
  70: 
  71:   // Fields.
  72:   // -------------------------------------------------------------------------
  73: 
  74:   public static final List ENCODINGS = Collections.unmodifiableList(
  75:     Arrays.asList(new String[] { "PkiPath", "PKCS7" }));
  76: 
  77:   private static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
  78:   private static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1");
  79: 
  80:   /** The certificate path. */
  81:   private List path;
  82: 
  83:   /** The cached PKCS #7 encoded bytes. */
  84:   private byte[] pkcs_encoded;
  85: 
  86:   /** The cached PkiPath encoded bytes. */
  87:   private byte[] pki_encoded;
  88: 
  89:   // Constructor.
  90:   // -------------------------------------------------------------------------
  91: 
  92:   public X509CertPath(List path)
  93:   {
  94:     super("X.509");
  95:     this.path = Collections.unmodifiableList(path);
  96:   }
  97: 
  98:   public X509CertPath(InputStream in) throws CertificateEncodingException
  99:   {
 100:     this(in, (String) ENCODINGS.get(0));
 101:   }
 102: 
 103:   public X509CertPath(InputStream in, String encoding)
 104:     throws CertificateEncodingException
 105:   {
 106:     super("X.509");
 107:     try
 108:       {
 109:         parse(in, encoding);
 110:       }
 111:     catch (IOException ioe)
 112:       {
 113:         throw new CertificateEncodingException();
 114:       }
 115:   }
 116: 
 117:   // Instance methods.
 118:   // -------------------------------------------------------------------------
 119: 
 120:   public List getCertificates()
 121:   {
 122:     return path; // already unmodifiable
 123:   }
 124: 
 125:   public byte[] getEncoded() throws CertificateEncodingException
 126:   {
 127:     return getEncoded((String) ENCODINGS.get(0));
 128:   }
 129: 
 130:   public byte[] getEncoded(String encoding) throws CertificateEncodingException
 131:   {
 132:     if (encoding.equalsIgnoreCase("PkiPath"))
 133:       {
 134:         if (pki_encoded == null)
 135:           {
 136:             try
 137:               {
 138:                 pki_encoded = encodePki();
 139:               }
 140:             catch (IOException ioe)
 141:               {
 142:                 throw new CertificateEncodingException();
 143:               }
 144:           }
 145:         return (byte[]) pki_encoded.clone();
 146:       }
 147:     else if (encoding.equalsIgnoreCase("PKCS7"))
 148:       {
 149:         if (pkcs_encoded == null)
 150:           {
 151:             try
 152:               {
 153:                 pkcs_encoded = encodePKCS();
 154:               }
 155:             catch (IOException ioe)
 156:               {
 157:                 throw new CertificateEncodingException();
 158:               }
 159:           }
 160:         return (byte[]) pkcs_encoded.clone();
 161:       }
 162:     else
 163:       throw new CertificateEncodingException("unknown encoding: " + encoding);
 164:   }
 165: 
 166:   public Iterator getEncodings()
 167:   {
 168:     return ENCODINGS.iterator(); // already unmodifiable
 169:   }
 170: 
 171:   // Own methods.
 172:   // -------------------------------------------------------------------------
 173: 
 174:   private void parse(InputStream in, String encoding)
 175:     throws CertificateEncodingException, IOException
 176:   {
 177:     DERReader der = new DERReader(in);
 178:     DERValue path = null;
 179:     if (encoding.equalsIgnoreCase("PkiPath"))
 180:       {
 181:         // PKI encoding is just a SEQUENCE of X.509 certificates.
 182:         path = der.read();
 183:         if (!path.isConstructed())
 184:           throw new DEREncodingException("malformed PkiPath");
 185:       }
 186:     else if (encoding.equalsIgnoreCase("PKCS7"))
 187:       {
 188:         // PKCS #7 encoding means that the certificates are contained in a
 189:         // SignedData PKCS #7 type.
 190:         //
 191:         // ContentInfo ::= SEQUENCE {
 192:         //   contentType ::= ContentType,
 193:         //   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
 194:         //
 195:         // ContentType ::= OBJECT IDENTIFIER
 196:         //
 197:         // SignedData ::= SEQUENCE {
 198:         //   version Version,
 199:         //   digestAlgorithms DigestAlgorithmIdentifiers,
 200:         //   contentInfo ContentInfo,
 201:         //   certificates [0] IMPLICIT ExtendedCertificatesAndCertificates
 202:         //                    OPTIONAL,
 203:         //   crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
 204:         //   signerInfos SignerInfos }
 205:         //
 206:         // Version ::= INTEGER
 207:         //
 208:         DERValue value = der.read();
 209:         if (!value.isConstructed())
 210:           throw new DEREncodingException("malformed ContentInfo");
 211:         value = der.read();
 212:         if (!(value.getValue() instanceof OID) ||
 213:             ((OID) value.getValue()).equals(PKCS7_SIGNED_DATA))
 214:           throw new DEREncodingException("not a SignedData");
 215:         value = der.read();
 216:         if (!value.isConstructed() || value.getTag() != 0)
 217:           throw new DEREncodingException("malformed content");
 218:         value = der.read();
 219:         if (value.getTag() != DER.INTEGER)
 220:           throw new DEREncodingException("malformed Version");
 221:         value = der.read();
 222:         if (!value.isConstructed() || value.getTag() != DER.SET)
 223:           throw new DEREncodingException("malformed DigestAlgorithmIdentifiers");
 224:         der.skip(value.getLength());
 225:         value = der.read();
 226:         if (!value.isConstructed())
 227:           throw new DEREncodingException("malformed ContentInfo");
 228:         der.skip(value.getLength());
 229:         path = der.read();
 230:         if (!path.isConstructed() || path.getTag() != 0)
 231:           throw new DEREncodingException("no certificates");
 232:       }
 233:     else
 234:       throw new CertificateEncodingException("unknown encoding: " + encoding);
 235: 
 236:     LinkedList certs = new LinkedList();
 237:     int len = 0;
 238:     while (len < path.getLength())
 239:       {
 240:         DERValue cert = der.read();
 241:         try
 242:           {
 243:             certs.add(new X509Certificate(new ByteArrayInputStream(cert.getEncoded())));
 244:           }
 245:         catch (CertificateException ce)
 246:           {
 247:             throw new CertificateEncodingException(ce.getMessage());
 248:           }
 249:         len += cert.getEncodedLength();
 250:         der.skip(cert.getLength());
 251:       }
 252: 
 253:     this.path = Collections.unmodifiableList(certs);
 254:   }
 255: 
 256:   private byte[] encodePki()
 257:     throws CertificateEncodingException, IOException
 258:   {
 259:     synchronized (path)
 260:       {
 261:         ByteArrayOutputStream out = new ByteArrayOutputStream();
 262:         for (Iterator i = path.iterator(); i.hasNext(); )
 263:           {
 264:             out.write(((Certificate) i.next()).getEncoded());
 265:           }
 266:         byte[] b = out.toByteArray();
 267:         DERValue val = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
 268:                                     b.length, b, null);
 269:         return val.getEncoded();
 270:       }
 271:   }
 272: 
 273:   private byte[] encodePKCS()
 274:     throws CertificateEncodingException, IOException
 275:   {
 276:     synchronized (path)
 277:       {
 278:         ArrayList signedData = new ArrayList(5);
 279:         signedData.add(new DERValue(DER.INTEGER, BigInteger.ONE));
 280:         signedData.add(new DERValue(DER.CONSTRUCTED | DER.SET,
 281:                                     Collections.EMPTY_SET));
 282:         signedData.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
 283:           Collections.singletonList(
 284:             new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_DATA))));
 285:         ByteArrayOutputStream out = new ByteArrayOutputStream();
 286:         for (Iterator i = path.iterator(); i.hasNext(); )
 287:           {
 288:             out.write(((Certificate) i.next()).getEncoded());
 289:           }
 290:         byte[] b = out.toByteArray();
 291:         signedData.add(new DERValue(DER.CONSTRUCTED | DER.CONTEXT,
 292:                                     b.length, b, null));
 293:         DERValue sdValue = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
 294:                                         signedData);
 295: 
 296:         ArrayList contentInfo = new ArrayList(2);
 297:         contentInfo.add(new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_SIGNED_DATA));
 298:         contentInfo.add(new DERValue(DER.CONSTRUCTED | DER.CONTEXT, sdValue));
 299:         return new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
 300:                             contentInfo).getEncoded();
 301:       }
 302:   }
 303: }