Home | History | Annotate | Download | only in jce
      1 package org.bouncycastle.jce;
      2 
      3 import java.io.ByteArrayInputStream;
      4 import java.io.ByteArrayOutputStream;
      5 import java.io.IOException;
      6 import java.math.BigInteger;
      7 import java.security.InvalidKeyException;
      8 import java.security.NoSuchAlgorithmException;
      9 import java.security.NoSuchProviderException;
     10 import java.security.PrivateKey;
     11 import java.security.Signature;
     12 import java.security.SignatureException;
     13 import java.security.cert.CRL;
     14 import java.security.cert.CRLException;
     15 import java.security.cert.Certificate;
     16 import java.security.cert.X509CRL;
     17 import java.security.cert.X509Certificate;
     18 import java.util.ArrayList;
     19 import java.util.Collection;
     20 import java.util.Enumeration;
     21 import java.util.HashSet;
     22 import java.util.Iterator;
     23 import java.util.Set;
     24 
     25 import org.bouncycastle.asn1.*;
     26 import org.bouncycastle.asn1.pkcs.ContentInfo;
     27 import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber;
     28 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
     29 import org.bouncycastle.asn1.pkcs.SignedData;
     30 import org.bouncycastle.asn1.pkcs.SignerInfo;
     31 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
     32 import org.bouncycastle.asn1.x509.CertificateList;
     33 import org.bouncycastle.asn1.x509.X509CertificateStructure;
     34 import org.bouncycastle.asn1.x509.X509Name;
     35 import org.bouncycastle.jce.provider.X509CRLObject;
     36 import org.bouncycastle.jce.provider.X509CertificateObject;
     37 
     38 /**
     39  * Represents a PKCS#7 object - specifically the "Signed Data"
     40  * type.
     41  * <p>
     42  * How to use it? To verify a signature, do:
     43  * <pre>
     44  * PKCS7SignedData pkcs7 = new PKCS7SignedData(der_bytes);        // Create it
     45  * pkcs7.update(bytes, 0, bytes.length);                          // Update checksum
     46  * boolean verified = pkcs7.verify();                             // Does it add up?
     47  *
     48  * To sign, do this:
     49  * PKCS7SignedData pkcs7 = new PKCS7SignedData(privKey, certChain, "MD5");
     50  * pkcs7.update(bytes, 0, bytes.length);                          // Update checksum
     51  * pkcs7.sign();                                                  // Create digest
     52  *
     53  * bytes = pkcs7.getEncoded();                                    // Write it somewhere
     54  * </pre>
     55  * <p>
     56  * This class is pretty close to obsolete, for a much better (and more complete)
     57  * implementation of PKCS7 have a look at the org.bouncycastle.cms package.
     58  * @deprecated this class really is obsolete - use the CMS package.
     59  */
     60 public class PKCS7SignedData
     61     implements PKCSObjectIdentifiers
     62 {
     63     private int version, signerversion;
     64     private Set digestalgos;
     65     private Collection certs, crls;
     66     private X509Certificate signCert;
     67     private byte[] digest;
     68     private String digestAlgorithm, digestEncryptionAlgorithm;
     69     private Signature sig;
     70     private transient PrivateKey privKey;
     71 
     72     private final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
     73     private final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
     74     private final String ID_MD5 = "1.2.840.113549.2.5";
     75     private final String ID_MD2 = "1.2.840.113549.2.2";
     76     private final String ID_SHA1 = "1.3.14.3.2.26";
     77     private final String ID_RSA = "1.2.840.113549.1.1.1";
     78     private final String ID_DSA = "1.2.840.10040.4.1";
     79 
     80     /**
     81      * Read an existing PKCS#7 object from a DER encoded byte array using
     82      * the BC provider.
     83      */
     84     public PKCS7SignedData(
     85         byte[]  in)
     86         throws SecurityException, CRLException, InvalidKeyException,
     87         NoSuchProviderException, NoSuchAlgorithmException
     88     {
     89         this(in, "BC");
     90     }
     91 
     92     /**
     93      * Read an existing PKCS#7 object from a DER encoded byte array
     94      */
     95     public PKCS7SignedData(
     96         byte[]  in,
     97         String  provider)
     98         throws SecurityException, CRLException, InvalidKeyException,
     99         NoSuchProviderException, NoSuchAlgorithmException
    100     {
    101         ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(in));
    102 
    103         //
    104         // Basic checks to make sure it's a PKCS#7 SignedData Object
    105         //
    106         DERObject pkcs;
    107 
    108         try
    109         {
    110             pkcs = din.readObject();
    111         }
    112         catch (IOException e)
    113         {
    114             throw new SecurityException("can't decode PKCS7SignedData object");
    115         }
    116 
    117         if (!(pkcs instanceof ASN1Sequence))
    118         {
    119             throw new SecurityException("Not a valid PKCS#7 object - not a sequence");
    120         }
    121 
    122         ContentInfo content = ContentInfo.getInstance(pkcs);
    123 
    124         if (!content.getContentType().equals(signedData))
    125         {
    126             throw new SecurityException("Not a valid PKCS#7 signed-data object - wrong header " + content.getContentType().getId());
    127         }
    128 
    129 
    130         SignedData  data = SignedData.getInstance(content.getContent());
    131 
    132         certs = new ArrayList();
    133 
    134         if (data.getCertificates() != null)
    135         {
    136             Enumeration ec = ASN1Set.getInstance(data.getCertificates()).getObjects();
    137 
    138             while (ec.hasMoreElements())
    139             {
    140                 certs.add(new X509CertificateObject(X509CertificateStructure.getInstance(ec.nextElement())));
    141             }
    142         }
    143 
    144         crls = new ArrayList();
    145 
    146         if (data.getCRLs() != null)
    147         {
    148             Enumeration ec = ASN1Set.getInstance(data.getCRLs()).getObjects();
    149             while (ec.hasMoreElements())
    150             {
    151                 crls.add(new X509CRLObject(CertificateList.getInstance(ec.nextElement())));
    152             }
    153         }
    154 
    155         version = data.getVersion().getValue().intValue();
    156 
    157         //
    158         // Get the digest algorithm
    159         //
    160         digestalgos = new HashSet();
    161         Enumeration e = data.getDigestAlgorithms().getObjects();
    162 
    163         while (e.hasMoreElements())
    164         {
    165             ASN1Sequence s = (ASN1Sequence)e.nextElement();
    166             DERObjectIdentifier o = (DERObjectIdentifier)s.getObjectAt(0);
    167             digestalgos.add(o.getId());
    168         }
    169 
    170         //
    171         // Get the SignerInfo
    172         //
    173         ASN1Set signerinfos = data.getSignerInfos();
    174         if (signerinfos.size() != 1)
    175         {
    176             throw new SecurityException("This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
    177         }
    178 
    179         SignerInfo signerInfo = SignerInfo.getInstance(signerinfos.getObjectAt(0));
    180 
    181         signerversion = signerInfo.getVersion().getValue().intValue();
    182 
    183         IssuerAndSerialNumber isAnds = signerInfo.getIssuerAndSerialNumber();
    184 
    185         //
    186         // Get the signing certificate
    187         //
    188         BigInteger      serialNumber = isAnds.getCertificateSerialNumber().getValue();
    189         X509Principal   issuer = new X509Principal(isAnds.getName());
    190 
    191         for (Iterator i = certs.iterator();i.hasNext();)
    192         {
    193             X509Certificate cert = (X509Certificate)i.next();
    194             if (serialNumber.equals(cert.getSerialNumber())
    195                     && issuer.equals(cert.getIssuerDN()))
    196             {
    197                 signCert = cert;
    198                 break;
    199             }
    200         }
    201 
    202         if (signCert == null)
    203         {
    204             throw new SecurityException("Can't find signing certificate with serial "+serialNumber.toString(16));
    205         }
    206 
    207         digestAlgorithm = signerInfo.getDigestAlgorithm().getObjectId().getId();
    208 
    209         digest = signerInfo.getEncryptedDigest().getOctets();
    210         digestEncryptionAlgorithm = signerInfo.getDigestEncryptionAlgorithm().getObjectId().getId();
    211 
    212         sig = Signature.getInstance(getDigestAlgorithm(), provider);
    213 
    214         sig.initVerify(signCert.getPublicKey());
    215     }
    216 
    217     /**
    218      * Create a new PKCS#7 object from the specified key using the BC provider.
    219      *
    220      * @param privKey the private key to be used for signing.
    221      * @param certChain the certificate chain associated with the private key.
    222      * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
    223      */
    224     public PKCS7SignedData(
    225         PrivateKey      privKey,
    226         Certificate[]   certChain,
    227         String          hashAlgorithm)
    228         throws SecurityException, InvalidKeyException,
    229         NoSuchProviderException, NoSuchAlgorithmException
    230     {
    231         this(privKey, certChain, hashAlgorithm, "BC");
    232     }
    233 
    234     /**
    235      * Create a new PKCS#7 object from the specified key.
    236      *
    237      * @param privKey the private key to be used for signing.
    238      * @param certChain the certificate chain associated with the private key.
    239      * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
    240      * @param provider the provider to use.
    241      */
    242     public PKCS7SignedData(
    243         PrivateKey      privKey,
    244         Certificate[]   certChain,
    245         String          hashAlgorithm,
    246         String          provider)
    247         throws SecurityException, InvalidKeyException,
    248         NoSuchProviderException, NoSuchAlgorithmException
    249     {
    250         this(privKey, certChain, null, hashAlgorithm, provider);
    251     }
    252 
    253     /**
    254      * Create a new PKCS#7 object from the specified key.
    255      *
    256      * @param privKey the private key to be used for signing.
    257      * @param certChain the certificate chain associated with the private key.
    258      * @param crlList the crl list associated with the private key.
    259      * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
    260      * @param provider the provider to use.
    261      */
    262     public PKCS7SignedData(
    263         PrivateKey      privKey,
    264         Certificate[]   certChain,
    265         CRL[]           crlList,
    266         String          hashAlgorithm,
    267         String          provider)
    268         throws SecurityException, InvalidKeyException,
    269         NoSuchProviderException, NoSuchAlgorithmException
    270     {
    271         this.privKey = privKey;
    272 
    273         if (hashAlgorithm.equals("MD5"))
    274         {
    275             digestAlgorithm = ID_MD5;
    276         }
    277         else if (hashAlgorithm.equals("MD2"))
    278         {
    279             digestAlgorithm = ID_MD2;
    280         }
    281         else if (hashAlgorithm.equals("SHA"))
    282         {
    283             digestAlgorithm = ID_SHA1;
    284         }
    285         else if (hashAlgorithm.equals("SHA1"))
    286         {
    287             digestAlgorithm = ID_SHA1;
    288         }
    289         else
    290         {
    291             throw new NoSuchAlgorithmException("Unknown Hash Algorithm "+hashAlgorithm);
    292         }
    293 
    294         version = signerversion = 1;
    295         certs = new ArrayList();
    296         crls = new ArrayList();
    297         digestalgos = new HashSet();
    298         digestalgos.add(digestAlgorithm);
    299 
    300         //
    301         // Copy in the certificates and crls used to sign the private key.
    302         //
    303         signCert = (X509Certificate)certChain[0];
    304         for (int i = 0;i < certChain.length;i++)
    305         {
    306             certs.add(certChain[i]);
    307         }
    308 
    309         if (crlList != null)
    310         {
    311             for (int i = 0;i < crlList.length;i++)
    312             {
    313                 crls.add(crlList[i]);
    314             }
    315         }
    316 
    317         //
    318         // Now we have private key, find out what the digestEncryptionAlgorithm is.
    319         //
    320         digestEncryptionAlgorithm = privKey.getAlgorithm();
    321         if (digestEncryptionAlgorithm.equals("RSA"))
    322         {
    323             digestEncryptionAlgorithm = ID_RSA;
    324         }
    325         else if (digestEncryptionAlgorithm.equals("DSA"))
    326         {
    327             digestEncryptionAlgorithm = ID_DSA;
    328         }
    329         else
    330         {
    331             throw new NoSuchAlgorithmException("Unknown Key Algorithm "+digestEncryptionAlgorithm);
    332         }
    333 
    334         sig = Signature.getInstance(getDigestAlgorithm(), provider);
    335 
    336         sig.initSign(privKey);
    337     }
    338 
    339     /**
    340      * Get the algorithm used to calculate the message digest
    341      */
    342     public String getDigestAlgorithm()
    343     {
    344         String da = digestAlgorithm;
    345         String dea = digestEncryptionAlgorithm;
    346 
    347         if (digestAlgorithm.equals(ID_MD5))
    348         {
    349             da = "MD5";
    350         }
    351         else if (digestAlgorithm.equals(ID_MD2))
    352         {
    353             da = "MD2";
    354         }
    355         else if (digestAlgorithm.equals(ID_SHA1))
    356         {
    357             da = "SHA1";
    358         }
    359 
    360         if (digestEncryptionAlgorithm.equals(ID_RSA))
    361         {
    362             dea = "RSA";
    363         }
    364         else if (digestEncryptionAlgorithm.equals(ID_DSA))
    365         {
    366             dea = "DSA";
    367         }
    368 
    369         return da + "with" + dea;
    370     }
    371 
    372     /**
    373      * Resets the PKCS7SignedData object to it's initial state, ready
    374      * to sign or verify a new buffer.
    375      */
    376     public void reset()
    377     {
    378         try
    379         {
    380             if (privKey==null)
    381             {
    382                 sig.initVerify(signCert.getPublicKey());
    383             }
    384             else
    385             {
    386                 sig.initSign(privKey);
    387             }
    388         }
    389         catch (Exception e)
    390         {
    391             throw new RuntimeException(e.toString());
    392         }
    393     }
    394 
    395     /**
    396      * Get the X.509 certificates associated with this PKCS#7 object
    397      */
    398     public Certificate[] getCertificates()
    399     {
    400         return (X509Certificate[])certs.toArray(new X509Certificate[certs.size()]);
    401     }
    402 
    403     /**
    404      * Get the X.509 certificate revocation lists associated with this PKCS#7 object
    405      */
    406     public Collection getCRLs()
    407     {
    408         return crls;
    409     }
    410 
    411     /**
    412      * Get the X.509 certificate actually used to sign the digest.
    413      */
    414     public X509Certificate getSigningCertificate()
    415     {
    416         return signCert;
    417     }
    418 
    419     /**
    420      * Get the version of the PKCS#7 object. Always 1
    421      */
    422     public int getVersion()
    423     {
    424         return version;
    425     }
    426 
    427     /**
    428      * Get the version of the PKCS#7 "SignerInfo" object. Always 1
    429      */
    430     public int getSigningInfoVersion()
    431     {
    432         return signerversion;
    433     }
    434 
    435     /**
    436      * Update the digest with the specified byte. This method is used both for signing and verifying
    437      */
    438     public void update(byte buf)
    439         throws SignatureException
    440     {
    441         sig.update(buf);
    442     }
    443 
    444     /**
    445      * Update the digest with the specified bytes. This method is used both for signing and verifying
    446      */
    447     public void update(byte[] buf, int off, int len)
    448         throws SignatureException
    449     {
    450         sig.update(buf, off, len);
    451     }
    452 
    453     /**
    454      * Verify the digest
    455      */
    456     public boolean verify()
    457         throws SignatureException
    458     {
    459         return sig.verify(digest);
    460     }
    461 
    462     /**
    463      * Get the "issuer" from the TBSCertificate bytes that are passed in
    464      */
    465     private DERObject getIssuer(byte[] enc)
    466     {
    467         try
    468         {
    469             ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(enc));
    470             ASN1Sequence seq = (ASN1Sequence)in.readObject();
    471             return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3 : 2);
    472         }
    473         catch (IOException e)
    474         {
    475             throw new Error("IOException reading from ByteArray: "+e);
    476         }
    477     }
    478 
    479     /**
    480      * return the bytes for the PKCS7SignedData object.
    481      */
    482     public byte[] getEncoded()
    483     {
    484         try
    485         {
    486 
    487             digest = sig.sign();
    488 
    489             // Create the set of Hash algorithms. I've assumed this is the
    490             // set of all hash agorithms used to created the digest in the
    491             // "signerInfo" structure. I may be wrong.
    492             //
    493             ASN1EncodableVector v = new ASN1EncodableVector();
    494             for (Iterator i = digestalgos.iterator(); i.hasNext();)
    495             {
    496                 AlgorithmIdentifier a = new AlgorithmIdentifier(
    497                             new DERObjectIdentifier((String)i.next()),
    498                             null);
    499 
    500                 v.add(a);
    501             }
    502 
    503             DERSet algos = new DERSet(v);
    504 
    505             // Create the contentInfo. Empty, I didn't implement this bit
    506             //
    507             DERSequence contentinfo = new DERSequence(
    508                                         new DERObjectIdentifier(ID_PKCS7_DATA));
    509 
    510             // Get all the certificates
    511             //
    512             v = new ASN1EncodableVector();
    513             for (Iterator i = certs.iterator();i.hasNext();)
    514             {
    515                 ASN1InputStream tempstream = new ASN1InputStream(new ByteArrayInputStream(((X509Certificate)i.next()).getEncoded()));
    516                 v.add(tempstream.readObject());
    517             }
    518 
    519             DERSet dercertificates = new DERSet(v);
    520 
    521             // Create signerinfo structure.
    522             //
    523             ASN1EncodableVector signerinfo = new ASN1EncodableVector();
    524 
    525             // Add the signerInfo version
    526             //
    527             signerinfo.add(new DERInteger(signerversion));
    528 
    529             IssuerAndSerialNumber isAnds = new IssuerAndSerialNumber(
    530                         new X509Name((ASN1Sequence)getIssuer(signCert.getTBSCertificate())),
    531                         new DERInteger(signCert.getSerialNumber()));
    532             signerinfo.add(isAnds);
    533 
    534             // Add the digestAlgorithm
    535             //
    536             // BEGIN android-changed
    537             signerinfo.add(new AlgorithmIdentifier(
    538                                 new DERObjectIdentifier(digestAlgorithm),
    539                                 DERNull.THE_ONE));
    540 
    541             //
    542             // Add the digestEncryptionAlgorithm
    543             //
    544             signerinfo.add(new AlgorithmIdentifier(
    545                                 new DERObjectIdentifier(digestEncryptionAlgorithm),
    546                                 DERNull.THE_ONE));
    547             // END android-changed
    548 
    549             //
    550             // Add the digest
    551             //
    552             signerinfo.add(new DEROctetString(digest));
    553 
    554 
    555             //
    556             // Finally build the body out of all the components above
    557             //
    558             ASN1EncodableVector body = new ASN1EncodableVector();
    559             body.add(new DERInteger(version));
    560             body.add(algos);
    561             body.add(contentinfo);
    562             body.add(new DERTaggedObject(false, 0, dercertificates));
    563 
    564             if (crls.size()>0)
    565             {
    566                 v = new ASN1EncodableVector();
    567                 for (Iterator i = crls.iterator();i.hasNext();)
    568                 {
    569                     ASN1InputStream t = new ASN1InputStream(new ByteArrayInputStream(((X509CRL)i.next()).getEncoded()));
    570                     v.add(t.readObject());
    571                 }
    572                 DERSet dercrls = new DERSet(v);
    573                 body.add(new DERTaggedObject(false, 1, dercrls));
    574             }
    575 
    576             // Only allow one signerInfo
    577             //
    578             body.add(new DERSet(new DERSequence(signerinfo)));
    579 
    580             // Now we have the body, wrap it in it's PKCS7Signed shell
    581             // and return it
    582             //
    583             ASN1EncodableVector whole = new ASN1EncodableVector();
    584             whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA));
    585             whole.add(new DERTaggedObject(0, new DERSequence(body)));
    586 
    587             ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
    588 
    589             DEROutputStream dout = new DEROutputStream(bOut);
    590             dout.writeObject(new DERSequence(whole));
    591             dout.close();
    592 
    593             return bOut.toByteArray();
    594         }
    595         catch (Exception e)
    596         {
    597             throw new RuntimeException(e.toString());
    598         }
    599     }
    600 }
    601