org.bouncycastle.cms.CMSSignedDataStreamGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail Show documentation
Show all versions of bcmail Show documentation
A patched bouncycastle-mail
The newest version!
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BERSequenceGenerator;
import org.bouncycastle.asn1.BERTaggedObject;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
/**
* General class for generating a pkcs7-signature message stream.
*
* A simple example of usage.
*
*
* X509Certificate signCert = ...
* certList.add(signCert);
*
* Store certs = new JcaCertStore(certList);
* ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate());
*
* CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
*
* gen.addSignerInfoGenerator(
* new JcaSignerInfoGeneratorBuilder(
* new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
* .build(sha1Signer, signCert));
*
* gen.addCertificates(certs);
*
* OutputStream sigOut = gen.open(bOut);
*
* sigOut.write("Hello World!".getBytes());
*
* sigOut.close();
*
*/
public class CMSSignedDataStreamGenerator
extends CMSSignedGenerator
{
private List _signerInfs = new ArrayList();
private List _messageDigests = new ArrayList();
private int _bufferSize;
private class DigestAndSignerInfoGeneratorHolder
{
SignerIntInfoGenerator signerInf;
MessageDigest digest;
String digestOID;
DigestAndSignerInfoGeneratorHolder(SignerIntInfoGenerator signerInf, MessageDigest digest, String digestOID)
{
this.signerInf = signerInf;
this.digest = digest;
this.digestOID = digestOID;
}
AlgorithmIdentifier getDigestAlgorithm()
{
return new AlgorithmIdentifier(new DERObjectIdentifier(digestOID), DERNull.INSTANCE);
}
}
private class SignerIntInfoGeneratorImpl
implements SignerIntInfoGenerator
{
private final SignerIdentifier _signerIdentifier;
private final String _encOID;
private final CMSAttributeTableGenerator _sAttr;
private final CMSAttributeTableGenerator _unsAttr;
private final String _encName;
private final Signature _sig;
SignerIntInfoGeneratorImpl(
PrivateKey key,
SignerIdentifier signerIdentifier,
String digestOID,
String encOID,
CMSAttributeTableGenerator sAttr,
CMSAttributeTableGenerator unsAttr,
Provider sigProvider,
SecureRandom random)
throws NoSuchAlgorithmException, InvalidKeyException
{
_signerIdentifier = signerIdentifier;
_encOID = encOID;
_sAttr = sAttr;
_unsAttr = unsAttr;
_encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(_encOID);
String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(digestOID);
String signatureName = digestName + "with" + _encName;
if (_sAttr != null)
{
_sig = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, sigProvider);
}
else
{
// Note: Need to use raw signatures here since we have already calculated the digest
if (_encName.equals("RSA"))
{
_sig = CMSSignedHelper.INSTANCE.getSignatureInstance("RSA", sigProvider);
}
else if (_encName.equals("DSA"))
{
_sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEwithDSA", sigProvider);
}
// TODO Add support for raw PSS
// else if (_encName.equals("RSAandMGF1"))
// {
// sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEWITHRSAPSS", _sigProvider);
// try
// {
// // Init the params this way to avoid having a 'raw' version of each PSS algorithm
// Signature sig2 = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, _sigProvider);
// PSSParameterSpec spec = (PSSParameterSpec)sig2.getParameters().getParameterSpec(PSSParameterSpec.class);
// sig.setParameter(spec);
// }
// catch (Exception e)
// {
// throw new SignatureException("algorithm: " + _encName + " could not be configured.");
// }
// }
else
{
throw new NoSuchAlgorithmException("algorithm: " + _encName + " not supported in base signatures.");
}
}
_sig.initSign(key, random);
}
public SignerInfo generate(DERObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
byte[] calculatedDigest) throws CMSStreamException
{
try
{
byte[] bytesToSign = calculatedDigest;
/* RFC 3852 5.4
* The result of the message digest calculation process depends on
* whether the signedAttrs field is present. When the field is absent,
* the result is just the message digest of the content as described
*
* above. When the field is present, however, the result is the message
* digest of the complete DER encoding of the SignedAttrs value
* contained in the signedAttrs field.
*/
ASN1Set signedAttr = null;
if (_sAttr != null)
{
Map parameters = getBaseParameters(contentType, digestAlgorithm, calculatedDigest);
AttributeTable signed = _sAttr.getAttributes(Collections.unmodifiableMap(parameters));
if (contentType == null) //counter signature
{
if (signed != null && signed.get(CMSAttributes.contentType) != null)
{
Hashtable tmpSigned = signed.toHashtable();
tmpSigned.remove(CMSAttributes.contentType);
signed = new AttributeTable(tmpSigned);
}
}
signedAttr = getAttributeSet(signed);
// sig must be composed from the DER encoding.
bytesToSign = signedAttr.getEncoded(ASN1Encodable.DER);
}
else
{
// Note: Need to use raw signatures here since we have already calculated the digest
if (_encName.equals("RSA"))
{
DigestInfo dInfo = new DigestInfo(digestAlgorithm, calculatedDigest);
bytesToSign = dInfo.getEncoded(ASN1Encodable.DER);
}
}
_sig.update(bytesToSign);
byte[] sigBytes = _sig.sign();
ASN1Set unsignedAttr = null;
if (_unsAttr != null)
{
Map parameters = getBaseParameters(contentType, digestAlgorithm, calculatedDigest);
parameters.put(CMSAttributeTableGenerator.SIGNATURE, sigBytes.clone());
AttributeTable unsigned = _unsAttr.getAttributes(Collections.unmodifiableMap(parameters));
unsignedAttr = getAttributeSet(unsigned);
}
AlgorithmIdentifier digestEncryptionAlgorithm = getEncAlgorithmIdentifier(_encOID, _sig);
return new SignerInfo(_signerIdentifier, digestAlgorithm,
signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr);
}
catch (IOException e)
{
throw new CMSStreamException("encoding error.", e);
}
catch (SignatureException e)
{
throw new CMSStreamException("error creating signature.", e);
}
}
}
/**
* base constructor
*/
public CMSSignedDataStreamGenerator()
{
}
/**
* constructor allowing specific source of randomness
* @param rand instance of SecureRandom to use
*/
public CMSSignedDataStreamGenerator(
SecureRandom rand)
{
super(rand);
}
/**
* Set the underlying string size for encapsulated data
*
* @param bufferSize length of octet strings to buffer the data.
*/
public void setBufferSize(
int bufferSize)
{
_bufferSize = bufferSize;
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, digestOID, CMSUtils.getProvider(sigProvider));
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
Provider sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, digestOID, new DefaultSignedAttributeTableGenerator(),
(CMSAttributeTableGenerator)null, sigProvider);
}
/**
* add a signer, specifying the digest encryption algorithm - no attributes other than the default ones will be
* provided here.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, encryptionOID, digestOID, CMSUtils.getProvider(sigProvider));
}
/**
* add a signer, specifying digest encryptionOID - no attributes other than the default ones will be
* provided here.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
Provider sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, encryptionOID, digestOID, new DefaultSignedAttributeTableGenerator(),
(CMSAttributeTableGenerator)null, sigProvider);
}
/**
* add a signer with extra signed/unsigned attributes.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, digestOID, signedAttr, unsignedAttr,
CMSUtils.getProvider(sigProvider));
}
/**
* add a signer with extra signed/unsigned attributes.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
addSigner(key, cert, digestOID, new DefaultSignedAttributeTableGenerator(signedAttr),
new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
}
/**
* add a signer with extra signed/unsigned attributes - specifying digest
* encryption algorithm.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, encryptionOID, digestOID, signedAttr, unsignedAttr,
CMSUtils.getProvider(sigProvider));
}
/**
* add a signer with extra signed/unsigned attributes and the digest encryption algorithm.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
addSigner(key, cert, encryptionOID, digestOID,
new DefaultSignedAttributeTableGenerator(signedAttr),
new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
}
/**
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, digestOID, signedAttrGenerator, unsignedAttrGenerator,
CMSUtils.getProvider(sigProvider));
}
/**
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttrGenerator,
unsignedAttrGenerator, sigProvider);
}
/**
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, cert, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator,
CMSUtils.getProvider(sigProvider));
}
/**
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
doAddSigner(key, getSignerIdentifier(cert), encryptionOID, digestOID, signedAttrGenerator,
unsignedAttrGenerator, sigProvider, sigProvider);
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, digestOID, CMSUtils.getProvider(sigProvider));
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
Provider sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, digestOID, new DefaultSignedAttributeTableGenerator(),
(CMSAttributeTableGenerator)null, sigProvider);
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignedInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String encryptionOID,
String digestOID,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, encryptionOID, digestOID, CMSUtils.getProvider(sigProvider));
}
/**
* add a signer - no attributes other than the default ones will be
* provided here, specifying the digest encryption algorithm.
*
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String encryptionOID,
String digestOID,
Provider sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, encryptionOID, digestOID,
new DefaultSignedAttributeTableGenerator(), (CMSAttributeTableGenerator)null,
sigProvider);
}
/**
* add a signer with extra signed/unsigned attributes.
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, digestOID, signedAttr, unsignedAttr,
CMSUtils.getProvider(sigProvider));
}
/**
* add a signer with extra signed/unsigned attributes.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
AttributeTable signedAttr,
AttributeTable unsignedAttr,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
addSigner(key, subjectKeyID, digestOID,
new DefaultSignedAttributeTableGenerator(signedAttr),
new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, digestOID, signedAttrGenerator, unsignedAttrGenerator,
CMSUtils.getProvider(sigProvider));
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttrGenerator,
unsignedAttrGenerator, sigProvider);
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
{
addSigner(key, subjectKeyID, encryptionOID, digestOID, signedAttrGenerator,
unsignedAttrGenerator, CMSUtils.getProvider(sigProvider));
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
doAddSigner(key, getSignerIdentifier(subjectKeyID), encryptionOID, digestOID,
signedAttrGenerator, unsignedAttrGenerator, sigProvider, sigProvider);
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
X509Certificate cert,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider,
Provider digProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
doAddSigner(key, getSignerIdentifier(cert), encryptionOID, digestOID,
signedAttrGenerator, unsignedAttrGenerator, sigProvider, digProvider);
}
/**
* @deprecated use addSignerInfoGenerator
*/
public void addSigner(
PrivateKey key,
byte[] subjectKeyID,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider,
Provider digProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
doAddSigner(key, getSignerIdentifier(subjectKeyID), encryptionOID, digestOID,
signedAttrGenerator, unsignedAttrGenerator, sigProvider, digProvider);
}
private void doAddSigner(
PrivateKey key,
SignerIdentifier signerIdentifier,
String encryptionOID,
String digestOID,
CMSAttributeTableGenerator signedAttrGenerator,
CMSAttributeTableGenerator unsignedAttrGenerator,
Provider sigProvider,
Provider digProvider)
throws NoSuchAlgorithmException, InvalidKeyException
{
String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(digestOID);
MessageDigest dig = CMSSignedHelper.INSTANCE.getDigestInstance(digestName, digProvider);
SignerIntInfoGeneratorImpl signerInf = new SignerIntInfoGeneratorImpl(key, signerIdentifier, digestOID, encryptionOID,
signedAttrGenerator, unsignedAttrGenerator, sigProvider, rand);
_signerInfs.add(new DigestAndSignerInfoGeneratorHolder(signerInf, dig, digestOID));
_messageDigests.add(dig);
}
/**
* generate a signed object that for a CMS Signed Data
* object using the given provider.
*/
public OutputStream open(
OutputStream out)
throws IOException
{
return open(out, false);
}
/**
* generate a signed object that for a CMS Signed Data
* object using the given provider - if encapsulate is true a copy
* of the message will be included in the signature with the
* default content type "data".
*/
public OutputStream open(
OutputStream out,
boolean encapsulate)
throws IOException
{
return open(CMSObjectIdentifiers.data, out, encapsulate);
}
/**
* generate a signed object that for a CMS Signed Data
* object using the given provider - if encapsulate is true a copy
* of the message will be included in the signature with the
* default content type "data". If dataOutputStream is non null the data
* being signed will be written to the stream as it is processed.
* @param out stream the CMS object is to be written to.
* @param encapsulate true if data should be encapsulated.
* @param dataOutputStream output stream to copy the data being signed to.
*/
public OutputStream open(
OutputStream out,
boolean encapsulate,
OutputStream dataOutputStream)
throws IOException
{
return open(CMSObjectIdentifiers.data, out, encapsulate, dataOutputStream);
}
/**
* @deprecated use open(ASN1ObjectIdentifier, OutputStream, boolean)
*/
public OutputStream open(
OutputStream out,
String eContentType,
boolean encapsulate)
throws IOException
{
return open(out, eContentType, encapsulate, null);
}
/**
* generate a signed object that for a CMS Signed Data
* object using the given provider - if encapsulate is true a copy
* of the message will be included in the signature. The content type
* is set according to the OID represented by the string signedContentType.
*/
public OutputStream open(
ASN1ObjectIdentifier eContentType,
OutputStream out,
boolean encapsulate)
throws IOException
{
return open(eContentType, out, encapsulate, null);
}
/**
* @deprecated use open(ASN1ObjectIdenfier, OutputStream, boolean, OutputStream)
*/
public OutputStream open(
OutputStream out,
String eContentType,
boolean encapsulate,
OutputStream dataOutputStream)
throws IOException
{
return open(new ASN1ObjectIdentifier(eContentType), out, encapsulate, dataOutputStream);
}
/**
* generate a signed object that for a CMS Signed Data
* object using the given provider - if encapsulate is true a copy
* of the message will be included in the signature. The content type
* is set according to the OID represented by the string signedContentType.
* @param eContentType OID for data to be signed.
* @param out stream the CMS object is to be written to.
* @param encapsulate true if data should be encapsulated.
* @param dataOutputStream output stream to copy the data being signed to.
*/
public OutputStream open(
ASN1ObjectIdentifier eContentType,
OutputStream out,
boolean encapsulate,
OutputStream dataOutputStream)
throws IOException
{
// TODO
// if (_signerInfs.isEmpty())
// {
// /* RFC 3852 5.2
// * "In the degenerate case where there are no signers, the
// * EncapsulatedContentInfo value being "signed" is irrelevant. In this
// * case, the content type within the EncapsulatedContentInfo value being
// * "signed" MUST be id-data (as defined in section 4), and the content
// * field of the EncapsulatedContentInfo value MUST be omitted."
// */
// if (encapsulate)
// {
// throw new IllegalArgumentException("no signers, encapsulate must be false");
// }
// if (!DATA.equals(eContentType))
// {
// throw new IllegalArgumentException("no signers, eContentType must be id-data");
// }
// }
//
// if (!DATA.equals(eContentType))
// {
// /* RFC 3852 5.3
// * [The 'signedAttrs']...
// * field is optional, but it MUST be present if the content type of
// * the EncapsulatedContentInfo value being signed is not id-data.
// */
// // TODO signedAttrs must be present for all signers
// }
//
// ContentInfo
//
BERSequenceGenerator sGen = new BERSequenceGenerator(out);
sGen.addObject(CMSObjectIdentifiers.signedData);
//
// Signed Data
//
BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
sigGen.addObject(calculateVersion(eContentType));
ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
//
// add the precalculated SignerInfo digest algorithms.
//
for (Iterator it = _signers.iterator(); it.hasNext();)
{
SignerInformation signer = (SignerInformation)it.next();
digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
}
//
// add the new digests
//
for (Iterator it = _signerInfs.iterator(); it.hasNext();)
{
DigestAndSignerInfoGeneratorHolder holder = (DigestAndSignerInfoGeneratorHolder)it.next();
digestAlgs.add(holder.getDigestAlgorithm());
}
for (Iterator it = signerGens.iterator(); it.hasNext();)
{
SignerInfoGenerator signerGen = (SignerInfoGenerator)it.next();
digestAlgs.add(signerGen.getDigestAlgorithm());
}
sigGen.getRawOutputStream().write(new DERSet(digestAlgs).getEncoded());
BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());
eiGen.addObject(eContentType);
// If encapsulating, add the data as an octet string in the sequence
OutputStream encapStream = encapsulate
? CMSUtils.createBEROctetOutputStream(eiGen.getRawOutputStream(), 0, true, _bufferSize)
: null;
// Also send the data to 'dataOutputStream' if necessary
OutputStream contentStream = CMSUtils.getSafeTeeOutputStream(dataOutputStream, encapStream);
// Let all the digests see the data as it is written
OutputStream digStream = CMSUtils.attachDigestsToOutputStream(_messageDigests, contentStream);
// Let all the signers see the data as it is written
OutputStream sigStream = CMSUtils.attachSignersToOutputStream(signerGens, digStream);
return new CmsSignedDataOutputStream(sigStream, eContentType, sGen, sigGen, eiGen);
}
// TODO Make public?
void generate(
OutputStream out,
String eContentType,
boolean encapsulate,
OutputStream dataOutputStream,
CMSProcessable content)
throws CMSException, IOException
{
OutputStream signedOut = open(out, eContentType, encapsulate, dataOutputStream);
if (content != null)
{
content.write(signedOut);
}
signedOut.close();
}
// RFC3852, section 5.1:
// IF ((certificates is present) AND
// (any certificates with a type of other are present)) OR
// ((crls is present) AND
// (any crls with a type of other are present))
// THEN version MUST be 5
// ELSE
// IF (certificates is present) AND
// (any version 2 attribute certificates are present)
// THEN version MUST be 4
// ELSE
// IF ((certificates is present) AND
// (any version 1 attribute certificates are present)) OR
// (any SignerInfo structures are version 3) OR
// (encapContentInfo eContentType is other than id-data)
// THEN version MUST be 3
// ELSE version MUST be 1
//
private DERInteger calculateVersion(
ASN1ObjectIdentifier contentOid)
{
boolean otherCert = false;
boolean otherCrl = false;
boolean attrCertV1Found = false;
boolean attrCertV2Found = false;
if (certs != null)
{
for (Iterator it = certs.iterator(); it.hasNext();)
{
Object obj = it.next();
if (obj instanceof ASN1TaggedObject)
{
ASN1TaggedObject tagged = (ASN1TaggedObject)obj;
if (tagged.getTagNo() == 1)
{
attrCertV1Found = true;
}
else if (tagged.getTagNo() == 2)
{
attrCertV2Found = true;
}
else if (tagged.getTagNo() == 3)
{
otherCert = true;
}
}
}
}
if (otherCert)
{
return new DERInteger(5);
}
if (crls != null) // no need to check if otherCert is true
{
for (Iterator it = crls.iterator(); it.hasNext();)
{
Object obj = it.next();
if (obj instanceof ASN1TaggedObject)
{
otherCrl = true;
}
}
}
if (otherCrl)
{
return new DERInteger(5);
}
if (attrCertV2Found)
{
return new DERInteger(4);
}
if (attrCertV1Found)
{
return new DERInteger(3);
}
if (checkForVersion3(_signers))
{
return new DERInteger(3);
}
if (!CMSObjectIdentifiers.data.equals(contentOid))
{
return new DERInteger(3);
}
return new DERInteger(1);
}
private boolean checkForVersion3(List signerInfos)
{
for (Iterator it = signerInfos.iterator(); it.hasNext();)
{
SignerInfo s = SignerInfo.getInstance(((SignerInformation)it.next()).toSignerInfo());
if (s.getVersion().getValue().intValue() == 3)
{
return true;
}
}
return false;
}
private class CmsSignedDataOutputStream
extends OutputStream
{
private OutputStream _out;
private ASN1ObjectIdentifier _contentOID;
private BERSequenceGenerator _sGen;
private BERSequenceGenerator _sigGen;
private BERSequenceGenerator _eiGen;
public CmsSignedDataOutputStream(
OutputStream out,
ASN1ObjectIdentifier contentOID,
BERSequenceGenerator sGen,
BERSequenceGenerator sigGen,
BERSequenceGenerator eiGen)
{
_out = out;
_contentOID = contentOID;
_sGen = sGen;
_sigGen = sigGen;
_eiGen = eiGen;
}
public void write(
int b)
throws IOException
{
_out.write(b);
}
public void write(
byte[] bytes,
int off,
int len)
throws IOException
{
_out.write(bytes, off, len);
}
public void write(
byte[] bytes)
throws IOException
{
_out.write(bytes);
}
public void close()
throws IOException
{
_out.close();
_eiGen.close();
digests.clear(); // clear the current preserved digest state
if (certs.size() != 0)
{
ASN1Set certSet = CMSUtils.createBerSetFromList(certs);
_sigGen.getRawOutputStream().write(new BERTaggedObject(false, 0, certSet).getEncoded());
}
if (crls.size() != 0)
{
ASN1Set crlSet = CMSUtils.createBerSetFromList(crls);
_sigGen.getRawOutputStream().write(new BERTaggedObject(false, 1, crlSet).getEncoded());
}
//
// collect all the SignerInfo objects
//
ASN1EncodableVector signerInfos = new ASN1EncodableVector();
//
// add the generated SignerInfo objects
//
{
Iterator it = _signerInfs.iterator();
while (it.hasNext())
{
DigestAndSignerInfoGeneratorHolder holder = (DigestAndSignerInfoGeneratorHolder)it.next();
byte[] calculatedDigest = holder.digest.digest();
digests.put(holder.digestOID, calculatedDigest.clone());
AlgorithmIdentifier digestAlgorithm = holder.getDigestAlgorithm();
signerInfos.add(holder.signerInf.generate(_contentOID, digestAlgorithm, calculatedDigest));
}
}
for (Iterator it = signerGens.iterator(); it.hasNext();)
{
SignerInfoGenerator sigGen = (SignerInfoGenerator)it.next();
try
{
signerInfos.add(sigGen.generate(_contentOID));
byte[] calculatedDigest = sigGen.getCalculatedDigest();
digests.put(sigGen.getDigestAlgorithm().getAlgorithm().getId(), calculatedDigest);
}
catch (CMSException e)
{
throw new CMSStreamException("exception generating signers: " + e.getMessage(), e);
}
}
//
// add the precalculated SignerInfo objects
//
{
Iterator it = _signers.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
// TODO Verify the content type and calculated digest match the precalculated SignerInfo
// if (!signer.getContentType().equals(_contentOID))
// {
// // TODO The precalculated content type did not match - error?
// }
//
// byte[] calculatedDigest = (byte[])_digests.get(signer.getDigestAlgOID());
// if (calculatedDigest == null)
// {
// // TODO We can't confirm this digest because we didn't calculate it - error?
// }
// else
// {
// if (!Arrays.areEqual(signer.getContentDigest(), calculatedDigest))
// {
// // TODO The precalculated digest did not match - error?
// }
// }
signerInfos.add(signer.toSignerInfo());
}
}
_sigGen.getRawOutputStream().write(new DERSet(signerInfos).getEncoded());
_sigGen.close();
_sGen.close();
}
}
}