package org.bouncycastle.cms; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.SignedData; import org.bouncycastle.asn1.cms.SignerInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.X509AttributeCertificateHolder; import org.bouncycastle.cert.X509CRLHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.util.Encodable; import org.bouncycastle.util.Store; /** * general class for handling a pkcs7-signature message. * * A simple example of usage - note, in the example below the validity of * the certificate isn't verified, just the fact that one of the certs * matches the given signer... * *
* Store certStore = s.getCertificates();
* SignerInformationStore signers = s.getSignerInfos();
* Collection c = signers.getSigners();
* Iterator it = c.iterator();
*
* while (it.hasNext())
* {
* SignerInformation signer = (SignerInformation)it.next();
* Collection certCollection = certStore.getMatches(signer.getSID());
*
* Iterator certIt = certCollection.iterator();
* X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
*
* if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
* {
* verified++;
* }
* }
*
*/
public class CMSSignedData
implements Encodable
{
private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
SignedData signedData;
ContentInfo contentInfo;
CMSTypedData signedContent;
SignerInformationStore signerInfoStore;
private Map hashes;
private CMSSignedData(
CMSSignedData c)
{
this.signedData = c.signedData;
this.contentInfo = c.contentInfo;
this.signedContent = c.signedContent;
this.signerInfoStore = c.signerInfoStore;
}
public CMSSignedData(
byte[] sigBlock)
throws CMSException
{
this(CMSUtils.readContentInfo(sigBlock));
}
public CMSSignedData(
CMSProcessable signedContent,
byte[] sigBlock)
throws CMSException
{
this(signedContent, CMSUtils.readContentInfo(sigBlock));
}
/**
* Content with detached signature, digests precomputed
*
* @param hashes a map of precomputed digests for content indexed by name of hash.
* @param sigBlock the signature object.
*/
public CMSSignedData(
Map hashes,
byte[] sigBlock)
throws CMSException
{
this(hashes, CMSUtils.readContentInfo(sigBlock));
}
/**
* base constructor - content with detached signature.
*
* @param signedContent the content that was signed.
* @param sigData the signature object.
*/
public CMSSignedData(
CMSProcessable signedContent,
InputStream sigData)
throws CMSException
{
this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
}
/**
* base constructor - with encapsulated content
*/
public CMSSignedData(
InputStream sigData)
throws CMSException
{
this(CMSUtils.readContentInfo(sigData));
}
public CMSSignedData(
final CMSProcessable signedContent,
ContentInfo sigData)
throws CMSException
{
if (signedContent instanceof CMSTypedData)
{
this.signedContent = (CMSTypedData)signedContent;
}
else
{
this.signedContent = new CMSTypedData()
{
public ASN1ObjectIdentifier getContentType()
{
return signedData.getEncapContentInfo().getContentType();
}
public void write(OutputStream out)
throws IOException, CMSException
{
signedContent.write(out);
}
public Object getContent()
{
return signedContent.getContent();
}
};
}
this.contentInfo = sigData;
this.signedData = getSignedData();
}
public CMSSignedData(
Map hashes,
ContentInfo sigData)
throws CMSException
{
this.hashes = hashes;
this.contentInfo = sigData;
this.signedData = getSignedData();
}
public CMSSignedData(
ContentInfo sigData)
throws CMSException
{
this.contentInfo = sigData;
this.signedData = getSignedData();
//
// this can happen if the signed message is sent simply to send a
// certificate chain.
//
ASN1Encodable content = signedData.getEncapContentInfo().getContent();
if (content != null)
{
if (content instanceof ASN1OctetString)
{
this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(),
((ASN1OctetString)content).getOctets());
}
else
{
this.signedContent = new PKCS7ProcessableObject(signedData.getEncapContentInfo().getContentType(), content);
}
}
else
{
this.signedContent = null;
}
}
private SignedData getSignedData()
throws CMSException
{
try
{
return SignedData.getInstance(contentInfo.getContent());
}
catch (ClassCastException e)
{
throw new CMSException("Malformed content.", e);
}
catch (IllegalArgumentException e)
{
throw new CMSException("Malformed content.", e);
}
}
/**
* Return the version number for this object
*/
public int getVersion()
{
return signedData.getVersion().getValue().intValue();
}
/**
* return the collection of signers that are associated with the
* signatures for the message.
*/
public SignerInformationStore getSignerInfos()
{
if (signerInfoStore == null)
{
ASN1Set s = signedData.getSignerInfos();
List signerInfos = new ArrayList();
for (int i = 0; i != s.size(); i++)
{
SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i));
ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType();
if (hashes == null)
{
signerInfos.add(new SignerInformation(info, contentType, signedContent, null));
}
else
{
Object obj = hashes.keySet().iterator().next();
byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm());
signerInfos.add(new SignerInformation(info, contentType, null, hash));
}
}
signerInfoStore = new SignerInformationStore(signerInfos);
}
return signerInfoStore;
}
/**
* Return if this is object represents a detached signature.
*
* @return true if this message represents a detached signature, false otherwise.
*/
public boolean isDetachedSignature()
{
return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() > 0;
}
/**
* Return if this is object represents a certificate management message.
*
* @return true if the message has no signers or content, false otherwise.
*/
public boolean isCertificateManagementMessage()
{
return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() == 0;
}
/**
* Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
*
* @return a Store of X509CertificateHolder objects.
*/
public Store