FileDocCategorySizeDatePackage
CMSSignedDataParser.javaAPI DocBouncy Castle Crypto API 1.41 (Java 1.5)25332Wed Oct 01 10:55:28 BST 2008org.bouncycastle.cms

CMSSignedDataParser

public class CMSSignedDataParser extends CMSContentInfoParser
Parsing class for an CMS Signed Data object from an input stream.

Note: that because we are in a streaming mode only one signer can be tried and it is important that the methods on the parser are called in the appropriate order.

A simple example of usage for an encapsulated signature.

Two notes: first, in the example below the validity of the certificate isn't verified, just the fact that one of the certs matches the given signer, and, second, because we are in a streaming mode the order of the operations is important.

CMSSignedDataParser sp = new CMSSignedDataParser(encapSigData);

sp.getSignedContent().drain();

CertStore certs = sp.getCertificatesAndCRLs("Collection", "BC");
SignerInformationStore signers = sp.getSignerInfos();

Collection c = signers.getSigners();
Iterator it = c.iterator();

while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getCertificates(signer.getSID());

Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate)certIt.next();

System.out.println("verify returns: " + signer.verify(cert, "BC"));
}
Note also: this class does not introduce buffering - if you are processing large files you should create the parser with:
CMSSignedDataParser ep = new CMSSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
where bufSize is a suitably large buffer size.

Fields Summary
private static final CMSSignedHelper
HELPER
private org.bouncycastle.asn1.cms.SignedDataParser
_signedData
private org.bouncycastle.asn1.DERObjectIdentifier
_signedContentType
private CMSTypedStream
_signedContent
private Map
_digests
private CertStore
_certStore
private SignerInformationStore
_signerInfoStore
private org.bouncycastle.x509.X509Store
_attributeStore
private org.bouncycastle.asn1.ASN1Set
_certSet
private org.bouncycastle.asn1.ASN1Set
_crlSet
private boolean
_isCertCrlParsed
private org.bouncycastle.x509.X509Store
_certificateStore
private org.bouncycastle.x509.X509Store
_crlStore
Constructors Summary
public CMSSignedDataParser(byte[] sigBlock)


     
              
         
    
        this(new ByteArrayInputStream(sigBlock));
    
public CMSSignedDataParser(CMSTypedStream signedContent, byte[] sigBlock)

        this(signedContent, new ByteArrayInputStream(sigBlock));
    
public CMSSignedDataParser(InputStream sigData)
base constructor - with encapsulated content

        this(null, sigData);
    
public CMSSignedDataParser(CMSTypedStream signedContent, InputStream sigData)
base constructor

param
signedContent the content that was signed.
param
sigData the signature object stream.

        super(sigData);
        
        try
        {
            _signedContent = signedContent;
            _signedData = SignedDataParser.getInstance(_contentInfo.getContent(DERTags.SEQUENCE));
            _digests = new HashMap();
            
            ASN1SetParser digAlgs = _signedData.getDigestAlgorithms();
            DEREncodable  o;
            
            while ((o = digAlgs.readObject()) != null)
            {
                AlgorithmIdentifier id = AlgorithmIdentifier.getInstance(o.getDERObject());
                try
                {
                    String        digestName = HELPER.getDigestAlgName(id.getObjectId().toString());
                    MessageDigest dig = HELPER.getDigestInstance(digestName, null);

                    this._digests.put(digestName, dig);
                }
                catch (NoSuchAlgorithmException e)
                {
                     //  ignore
                }
            }

            //
            // If the message is simply a certificate chain message getContent() may return null.
            //
            ContentInfoParser     cont = _signedData.getEncapContentInfo();
            ASN1OctetStringParser octs = (ASN1OctetStringParser)
                cont.getContent(DERTags.OCTET_STRING);

            if (octs != null)
            {
                CMSTypedStream ctStr = new CMSTypedStream(
                    cont.getContentType().getId(), octs.getOctetStream());

                if (_signedContent == null)
                {
                    _signedContent = ctStr; 
                }
                else
                {
                    //
                    // content passed in, need to read past empty encapsulated content info object if present
                    //
                    ctStr.drain();
                }
            }

            if (signedContent == null)
            {
                _signedContentType = cont.getContentType();
            }
            else
            {
                _signedContentType = new DERObjectIdentifier(_signedContent.getContentType());
            }
        }
        catch (IOException e)
        {
            throw new CMSException("io exception: " + e.getMessage(), e);
        }
        
        if (_digests.isEmpty())
        {
            throw new CMSException("no digests could be created for message.");
        }
    
Methods Summary
private static org.bouncycastle.asn1.ASN1SetgetASN1Set(org.bouncycastle.asn1.ASN1SetParser asn1SetParser)

        return asn1SetParser == null
            ?   null
            :   ASN1Set.getInstance(asn1SetParser.getDERObject());
    
public org.bouncycastle.x509.X509StoregetAttributeCertificates(java.lang.String type, java.lang.String provider)
return a X509Store containing the attribute certificates, if any, contained in this message.

param
type type of store to create
param
provider name of provider to use
return
a store of attribute certificates
exception
NoSuchProviderException if the provider requested isn't available.
exception
org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        return getAttributeCertificates(type, CMSUtils.getProvider(provider));
    
public org.bouncycastle.x509.X509StoregetAttributeCertificates(java.lang.String type, java.security.Provider provider)
return a X509Store containing the attribute certificates, if any, contained in this message.

param
type type of store to create
param
provider provider to use
return
a store of attribute certificates
exception
org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        if (_attributeStore == null)
        {
            populateCertCrlSets();

            _attributeStore = HELPER.createAttributeStore(type, provider, _certSet);
        }

        return _attributeStore;
    
public org.bouncycastle.x509.X509StoregetCRLs(java.lang.String type, java.lang.String provider)
return a X509Store containing CRLs, if any, contained in this message.

param
type type of store to create
param
provider name of provider to use
return
a store of CRLs
exception
NoSuchProviderException if the provider requested isn't available.
exception
NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        return getCRLs(type, CMSUtils.getProvider(provider));
    
public org.bouncycastle.x509.X509StoregetCRLs(java.lang.String type, java.security.Provider provider)
return a X509Store containing CRLs, if any, contained in this message.

param
type type of store to create
param
provider provider to use
return
a store of CRLs
exception
NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        if (_crlStore == null)
        {
            populateCertCrlSets();

            _crlStore = HELPER.createCRLsStore(type, provider, _crlSet);
        }

        return _crlStore;
    
public org.bouncycastle.x509.X509StoregetCertificates(java.lang.String type, java.security.Provider provider)
return a X509Store containing the public key certificates, if any, contained in this message.

param
type type of store to create
param
provider provider to use
return
a store of public key certificates
exception
NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        if (_certificateStore == null)
        {
            populateCertCrlSets();

            _certificateStore = HELPER.createCertificateStore(type, provider, _certSet);
        }

        return _certificateStore;
    
public org.bouncycastle.x509.X509StoregetCertificates(java.lang.String type, java.lang.String provider)
return a X509Store containing the public key certificates, if any, contained in this message.

param
type type of store to create
param
provider provider to use
return
a store of public key certificates
exception
NoSuchProviderException if the provider requested isn't available.
exception
NoSuchStoreException if the store type isn't available.
exception
CMSException if a general exception prevents creation of the X509Store

        return getCertificates(type, CMSUtils.getProvider(provider));
    
public java.security.cert.CertStoregetCertificatesAndCRLs(java.lang.String type, java.lang.String provider)
return a CertStore containing the certificates and CRLs associated with this message.

exception
NoSuchProviderException if the provider requested isn't available.
exception
NoSuchAlgorithmException if the cert store isn't available.
exception
CMSException if a general exception prevents creation of the CertStore

        return getCertificatesAndCRLs(type, CMSUtils.getProvider(provider));
    
public java.security.cert.CertStoregetCertificatesAndCRLs(java.lang.String type, java.security.Provider provider)
return a CertStore containing the certificates and CRLs associated with this message.

exception
NoSuchProviderException if the provider requested isn't available.
exception
NoSuchAlgorithmException if the cert store isn't available.
exception
CMSException if a general exception prevents creation of the CertStore

        if (_certStore == null)
        {
            populateCertCrlSets();

            _certStore = HELPER.createCertStore(type, provider, _certSet, _crlSet);
        }

        return _certStore;
    
public CMSTypedStreamgetSignedContent()

        if (_signedContent != null)
        {
            InputStream digStream = _signedContent.getContentStream();
            
            Iterator it = _digests.values().iterator();
            
            while (it.hasNext())
            {
                digStream = new DigestInputStream(digStream, (MessageDigest)it.next());
            }
            
            return new CMSTypedStream(_signedContent.getContentType(), digStream);
        }
        else
        {
            return null;
        }
    
public java.lang.StringgetSignedContentTypeOID()
Return the a string representation of the OID associated with the encapsulated content info structure carried in the signed data.

return
the OID for the content type.

        return _signedContentType.getId();
    
public SignerInformationStoregetSignerInfos()
return the collection of signers that are associated with the signatures for the message.

throws
CMSException

        if (_signerInfoStore == null)
        {
            populateCertCrlSets();
            
            List      signerInfos = new ArrayList();
            Map       hashes = new HashMap();
            
            Iterator  it = _digests.keySet().iterator();
            while (it.hasNext())
            {
                Object digestKey = it.next();
                
                hashes.put(digestKey, ((MessageDigest)_digests.get(digestKey)).digest());
            }
            
            try
            {
                ASN1SetParser     s = _signedData.getSignerInfos();
                DEREncodable      o;
                
                while ((o = s.readObject()) != null)
                {
                    SignerInfo info = SignerInfo.getInstance(o.getDERObject());
                    String     digestName = HELPER.getDigestAlgName(info.getDigestAlgorithm().getObjectId().getId());
                    
                    byte[] hash = (byte[])hashes.get(digestName);
                    
                    signerInfos.add(new SignerInformation(info, _signedContentType, null, new BaseDigestCalculator(hash)));
                }
            }
            catch (IOException e)
            {
                throw new CMSException("io exception: " + e.getMessage(), e);
            }

            _signerInfoStore = new SignerInformationStore(signerInfos);
        }

        return _signerInfoStore;
    
public intgetVersion()
Return the version number for the SignedData object

return
the version number

        return _signedData.getVersion().getValue().intValue();
    
private static org.bouncycastle.asn1.x509.AlgorithmIdentifiermakeAlgId(java.lang.String oid, byte[] params)

        if (params != null)
        {
            return new AlgorithmIdentifier(
                            new DERObjectIdentifier(oid), makeObj(params));
        }
        else
        {
            return new AlgorithmIdentifier(
                            new DERObjectIdentifier(oid), new DERNull());
        }
    
private static org.bouncycastle.asn1.DERObjectmakeObj(byte[] encoding)

        if (encoding == null)
        {
            return null;
        }

        ASN1InputStream         aIn = new ASN1InputStream(encoding);

        return aIn.readObject();
    
private static voidpipeOctetString(org.bouncycastle.asn1.ASN1OctetStringParser octs, java.io.OutputStream output)

        BEROctetStringGenerator octGen = new BEROctetStringGenerator(output, 0, true);
        // TODO Allow specification of a specific fragment size?
        OutputStream outOctets = octGen.getOctetOutputStream();
        Streams.pipeAll(octs.getOctetStream(), outOctets);
        outOctets.close();
    
private voidpopulateCertCrlSets()

        if (_isCertCrlParsed)
        {
            return;
        }

        _isCertCrlParsed = true;

        try
        {
            // care! Streaming - these must be done in exactly this order.
            _certSet = getASN1Set(_signedData.getCertificates());
            _crlSet = getASN1Set(_signedData.getCrls());
        }
        catch (IOException e)
        {
            throw new CMSException("problem parsing cert/crl sets", e);
        }
    
public static java.io.OutputStreamreplaceCertificatesAndCRLs(java.io.InputStream original, java.security.cert.CertStore certsAndCrls, java.io.OutputStream out)
Replace the certificate and CRL information associated with this CMSSignedData object with the new one passed in.

The output stream is returned unclosed.

param
original the signed data stream to be used as a base.
param
certsAndCrls the new certificates and CRLs to be used.
param
out the stream to write the new signed data object to.
return
out.
exception
CMSException if there is an error processing the CertStore

        ASN1StreamParser in = new ASN1StreamParser(original, CMSUtils.getMaximumMemory());
        ContentInfoParser contentInfo = new ContentInfoParser((ASN1SequenceParser)in.readObject());
        SignedDataParser signedData = SignedDataParser.getInstance(contentInfo.getContent(DERTags.SEQUENCE));

        BERSequenceGenerator sGen = new BERSequenceGenerator(out);

        sGen.addObject(CMSObjectIdentifiers.signedData);

        BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);

        // version number
        sigGen.addObject(signedData.getVersion());

        // digests
        sigGen.getRawOutputStream().write(signedData.getDigestAlgorithms().getDERObject().getEncoded());

        // encap content info
        ContentInfoParser encapContentInfo = signedData.getEncapContentInfo();

        BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());

        eiGen.addObject(encapContentInfo.getContentType());

        ASN1OctetStringParser octs = (ASN1OctetStringParser)
            encapContentInfo.getContent(DERTags.OCTET_STRING);

        if (octs != null)
        {
            pipeOctetString(octs, eiGen.getRawOutputStream());
        }

        eiGen.close();

        //
        // skip existing certs and CRLs
        //
        getASN1Set(signedData.getCertificates());
        getASN1Set(signedData.getCrls());

        //
        // replace the certs and crls in the SignedData object
        //
        ASN1Set certs;

        try
        {
            certs = CMSUtils.createBerSetFromList(CMSUtils.getCertificatesFromStore(certsAndCrls));
        }
        catch (CertStoreException e)
        {
            throw new CMSException("error getting certs from certStore", e);
        }

        if (certs.size() > 0)
        {
            sigGen.getRawOutputStream().write(new DERTaggedObject(false, 0, certs).getEncoded());
        }

        ASN1Set crls;

        try
        {
            crls = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(certsAndCrls));
        }
        catch (CertStoreException e)
        {
            throw new CMSException("error getting crls from certStore", e);
        }

        if (crls.size() > 0)
        {
            sigGen.getRawOutputStream().write(new DERTaggedObject(false, 1, crls).getEncoded());
        }

        sigGen.getRawOutputStream().write(signedData.getSignerInfos().getDERObject().getEncoded());

        sigGen.close();

        sGen.close();

        return out;
    
public static java.io.OutputStreamreplaceSigners(java.io.InputStream original, SignerInformationStore signerInformationStore, java.io.OutputStream out)
Replace the signerinformation store associated with the passed in message contained in the stream original with the new one passed in. You would probably only want to do this if you wanted to change the unsigned attributes associated with a signer, or perhaps delete one.

The output stream is returned unclosed.

param
original the signed data stream to be used as a base.
param
signerInformationStore the new signer information store to use.
param
out the stream to write the new signed data object to.
return
out.

        ASN1StreamParser in = new ASN1StreamParser(original, CMSUtils.getMaximumMemory());
        ContentInfoParser contentInfo = new ContentInfoParser((ASN1SequenceParser)in.readObject());
        SignedDataParser signedData = SignedDataParser.getInstance(contentInfo.getContent(DERTags.SEQUENCE));

        BERSequenceGenerator sGen = new BERSequenceGenerator(out);

        sGen.addObject(CMSObjectIdentifiers.signedData);

        BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);

        // version number
        sigGen.addObject(signedData.getVersion());

        // digests
        signedData.getDigestAlgorithms().getDERObject();  // skip old ones

        ASN1EncodableVector digestAlgs = new ASN1EncodableVector();

        for (Iterator it = signerInformationStore.getSigners().iterator(); it.hasNext();)
        {
            SignerInformation        signer = (SignerInformation)it.next();
            AlgorithmIdentifier     digAlgId;

            digAlgId = makeAlgId(signer.getDigestAlgOID(), signer.getDigestAlgParams());

            digestAlgs.add(digAlgId);
        }

        sigGen.getRawOutputStream().write(new DERSet(digestAlgs).getEncoded());

        // encap content info
        ContentInfoParser encapContentInfo = signedData.getEncapContentInfo();

        BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());

        eiGen.addObject(encapContentInfo.getContentType());

        ASN1OctetStringParser octs = (ASN1OctetStringParser)
            encapContentInfo.getContent(DERTags.OCTET_STRING);

        if (octs != null)
        {
            pipeOctetString(octs, eiGen.getRawOutputStream());
        }

        eiGen.close();


        writeSetToGeneratorTagged(sigGen, signedData.getCertificates(), 0);
        writeSetToGeneratorTagged(sigGen, signedData.getCrls(), 1);


        ASN1EncodableVector signerInfos = new ASN1EncodableVector();
        for (Iterator it = signerInformationStore.getSigners().iterator(); it.hasNext();)
        {
            SignerInformation        signer = (SignerInformation)it.next();

            signerInfos.add(signer.toSignerInfo());
        }

        sigGen.getRawOutputStream().write(new DERSet(signerInfos).getEncoded());

        sigGen.close();

        sGen.close();

        return out;
    
private static voidwriteSetToGeneratorTagged(org.bouncycastle.asn1.ASN1Generator asn1Gen, org.bouncycastle.asn1.ASN1SetParser asn1SetParser, int tagNo)

        ASN1Set asn1Set = getASN1Set(asn1SetParser);

        if (asn1Set != null)
        {
            ASN1TaggedObject taggedObj = (asn1SetParser instanceof BERSetParser)
                ?   new BERTaggedObject(false, tagNo, asn1Set)
                :   new DERTaggedObject(false, tagNo, asn1Set);

            asn1Gen.getRawOutputStream().write(taggedObj.getEncoded());                
        }