org.bouncycastle.cms.CMSAuthenticatedDataStreamGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail-jdk16 Show documentation
Show all versions of bcmail-jdk16 Show documentation
The Bouncy Castle Java CMS and S/MIME APIs for handling the CMS and S/MIME protocols. This jar contains CMS and S/MIME APIs for JDK 1.6. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs. If the S/MIME API is used, the JavaMail API and the Java activation framework will also be needed.
The newest version!
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.BERSequenceGenerator;
import org.bouncycastle.asn1.BERSet;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.cms.AuthenticatedData;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.GenericKey;
import org.bouncycastle.operator.MacCalculator;
import org.bouncycastle.util.io.TeeOutputStream;
/**
* General class for generating a CMS authenticated-data message stream.
*
* A simple example of usage.
*
* CMSAuthenticatedDataStreamGenerator edGen = new CMSAuthenticatedDataStreamGenerator();
*
* edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC"));
*
* ByteArrayOutputStream bOut = new ByteArrayOutputStream();
*
* OutputStream out = edGen.open(
* bOut, new JceCMSMacCalculatorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider("BC").build());*
* out.write(data);
*
* out.close();
*
*/
public class CMSAuthenticatedDataStreamGenerator
extends CMSAuthenticatedGenerator
{
// Currently not handled
// private Object _originatorInfo = null;
// private Object _unprotectedAttributes = null;
private int bufferSize;
private boolean berEncodeRecipientSet;
private MacCalculator macCalculator;
/**
* base constructor
*/
public CMSAuthenticatedDataStreamGenerator()
{
}
/**
* Set the underlying string size for encapsulated data
*
* @param bufferSize length of octet strings to buffer the data.
*/
public void setBufferSize(
int bufferSize)
{
this.bufferSize = bufferSize;
}
/**
* Use a BER Set to store the recipient information. By default recipients are
* stored in a DER encoding.
*
* @param useBerEncodingForRecipients true if a BER set should be used, false if DER.
*/
public void setBEREncodeRecipients(
boolean useBerEncodingForRecipients)
{
berEncodeRecipientSet = useBerEncodingForRecipients;
}
/**
* generate an authenticated data structure with the encapsulated bytes marked as DATA.
*
* @param out the stream to store the authenticated structure in.
* @param macCalculator calculator for the MAC to be attached to the data.
*/
public OutputStream open(
OutputStream out,
MacCalculator macCalculator)
throws CMSException
{
return open(CMSObjectIdentifiers.data, out, macCalculator);
}
public OutputStream open(
OutputStream out,
MacCalculator macCalculator,
DigestCalculator digestCalculator)
throws CMSException
{
return open(CMSObjectIdentifiers.data, out, macCalculator, digestCalculator);
}
/**
* generate an authenticated data structure with the encapsulated bytes marked as type dataType.
*
* @param dataType the type of the data been written to the object.
* @param out the stream to store the authenticated structure in.
* @param macCalculator calculator for the MAC to be attached to the data.
*/
public OutputStream open(
ASN1ObjectIdentifier dataType,
OutputStream out,
MacCalculator macCalculator)
throws CMSException
{
return open(dataType, out, macCalculator, null);
}
/**
* generate an authenticated data structure with the encapsulated bytes marked as type dataType.
*
* @param dataType the type of the data been written to the object.
* @param out the stream to store the authenticated structure in.
* @param macCalculator calculator for the MAC to be attached to the data.
* @param digestCalculator calculator for computing digest of the encapsulated data.
*/
public OutputStream open(
ASN1ObjectIdentifier dataType,
OutputStream out,
MacCalculator macCalculator,
DigestCalculator digestCalculator)
throws CMSException
{
this.macCalculator = macCalculator;
try
{
ASN1EncodableVector recipientInfos = new ASN1EncodableVector();
for (Iterator it = recipientInfoGenerators.iterator(); it.hasNext();)
{
RecipientInfoGenerator recipient = (RecipientInfoGenerator)it.next();
recipientInfos.add(recipient.generate(macCalculator.getKey()));
}
//
// ContentInfo
//
BERSequenceGenerator cGen = new BERSequenceGenerator(out);
cGen.addObject(CMSObjectIdentifiers.authenticatedData);
//
// Authenticated Data
//
BERSequenceGenerator authGen = new BERSequenceGenerator(cGen.getRawOutputStream(), 0, true);
authGen.addObject(new DERInteger(AuthenticatedData.calculateVersion(null)));
if (berEncodeRecipientSet)
{
authGen.getRawOutputStream().write(new BERSet(recipientInfos).getEncoded());
}
else
{
authGen.getRawOutputStream().write(new DERSet(recipientInfos).getEncoded());
}
AlgorithmIdentifier macAlgId = macCalculator.getAlgorithmIdentifier();
authGen.getRawOutputStream().write(macAlgId.getEncoded());
if (digestCalculator != null)
{
authGen.addObject(new DERTaggedObject(false, 1, digestCalculator.getAlgorithmIdentifier()));
}
BERSequenceGenerator eiGen = new BERSequenceGenerator(authGen.getRawOutputStream());
eiGen.addObject(dataType);
OutputStream octetStream = CMSUtils.createBEROctetOutputStream(
eiGen.getRawOutputStream(), 0, false, bufferSize);
OutputStream mOut;
if (digestCalculator != null)
{
mOut = new TeeOutputStream(octetStream, digestCalculator.getOutputStream());
}
else
{
mOut = new TeeOutputStream(octetStream, macCalculator.getOutputStream());
}
return new CmsAuthenticatedDataOutputStream(macCalculator, digestCalculator, dataType, mOut, cGen, authGen, eiGen);
}
catch (IOException e)
{
throw new CMSException("exception decoding algorithm parameters.", e);
}
}
private class CmsAuthenticatedDataOutputStream
extends OutputStream
{
private OutputStream dataStream;
private BERSequenceGenerator cGen;
private BERSequenceGenerator envGen;
private BERSequenceGenerator eiGen;
private MacCalculator macCalculator;
private DigestCalculator digestCalculator;
private ASN1ObjectIdentifier contentType;
public CmsAuthenticatedDataOutputStream(
MacCalculator macCalculator,
DigestCalculator digestCalculator,
ASN1ObjectIdentifier contentType,
OutputStream dataStream,
BERSequenceGenerator cGen,
BERSequenceGenerator envGen,
BERSequenceGenerator eiGen)
{
this.macCalculator = macCalculator;
this.digestCalculator = digestCalculator;
this.contentType = contentType;
this.dataStream = dataStream;
this.cGen = cGen;
this.envGen = envGen;
this.eiGen = eiGen;
}
public void write(
int b)
throws IOException
{
dataStream.write(b);
}
public void write(
byte[] bytes,
int off,
int len)
throws IOException
{
dataStream.write(bytes, off, len);
}
public void write(
byte[] bytes)
throws IOException
{
dataStream.write(bytes);
}
public void close()
throws IOException
{
dataStream.close();
eiGen.close();
Map parameters;
if (digestCalculator != null)
{
parameters = Collections.unmodifiableMap(getBaseParameters(contentType, digestCalculator.getAlgorithmIdentifier(), digestCalculator.getDigest()));
if (authGen == null)
{
authGen = new DefaultAuthenticatedAttributeTableGenerator();
}
ASN1Set authed = new DERSet(authGen.getAttributes(parameters).toASN1EncodableVector());
OutputStream mOut = macCalculator.getOutputStream();
mOut.write(authed.getDEREncoded());
mOut.close();
envGen.addObject(new DERTaggedObject(false, 2, authed));
}
else
{
parameters = Collections.unmodifiableMap(new HashMap());
}
envGen.addObject(new DEROctetString(macCalculator.getMac()));
if (unauthGen != null)
{
envGen.addObject(new DERTaggedObject(false, 3, new BERSet(unauthGen.getAttributes(parameters).toASN1EncodableVector())));
}
envGen.close();
cGen.close();
}
}
/**
* constructor allowing specific source of randomness
* @param rand instance of SecureRandom to use
* @deprecated no longer of any use, use basic constructor.
*/
public CMSAuthenticatedDataStreamGenerator(
SecureRandom rand)
{
super(rand);
}
private class OldCmsAuthenticatedDataOutputStream
extends OutputStream
{
private OutputStream dataStream;
private Mac mac;
private BERSequenceGenerator cGen;
private BERSequenceGenerator envGen;
private BERSequenceGenerator eiGen;
public OldCmsAuthenticatedDataOutputStream(
OutputStream dataStream,
Mac mac,
BERSequenceGenerator cGen,
BERSequenceGenerator envGen,
BERSequenceGenerator eiGen)
{
this.dataStream = dataStream;
this.mac = mac;
this.cGen = cGen;
this.envGen = envGen;
this.eiGen = eiGen;
}
public void write(
int b)
throws IOException
{
dataStream.write(b);
}
public void write(
byte[] bytes,
int off,
int len)
throws IOException
{
dataStream.write(bytes, off, len);
}
public void write(
byte[] bytes)
throws IOException
{
dataStream.write(bytes);
}
public void close()
throws IOException
{
dataStream.close();
eiGen.close();
// [TODO] auth attributes go here
envGen.addObject(new DEROctetString(mac.doFinal()));
// [TODO] unauth attributes go here
envGen.close();
cGen.close();
}
}
/**
* generate an enveloped object that contains an CMS Enveloped Data
* object using the given provider and the passed in key generator.
* @throws java.io.IOException
* @deprecated
*/
private OutputStream open(
OutputStream out,
String macOID,
KeyGenerator keyGen,
Provider provider)
throws NoSuchAlgorithmException, CMSException
{
Provider encProvider = keyGen.getProvider();
SecretKey encKey = keyGen.generateKey();
AlgorithmParameterSpec params = generateParameterSpec(macOID, encKey, encProvider);
Iterator it = oldRecipientInfoGenerators.iterator();
ASN1EncodableVector recipientInfos = new ASN1EncodableVector();
while (it.hasNext())
{
IntRecipientInfoGenerator recipient = (IntRecipientInfoGenerator)it.next();
try
{
recipientInfos.add(recipient.generate(encKey, rand, provider));
}
catch (InvalidKeyException e)
{
throw new CMSException("key inappropriate for algorithm.", e);
}
catch (GeneralSecurityException e)
{
throw new CMSException("error making encrypted content.", e);
}
}
for (it = recipientInfoGenerators.iterator(); it.hasNext();)
{
RecipientInfoGenerator recipient = (RecipientInfoGenerator)it.next();
recipientInfos.add(recipient.generate(new GenericKey(encKey)));
}
return open(out, macOID, encKey, params, recipientInfos, encProvider);
}
protected OutputStream open(
OutputStream out,
String macOID,
SecretKey encKey,
AlgorithmParameterSpec params,
ASN1EncodableVector recipientInfos,
String provider)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
{
return open(out, macOID, encKey, params, recipientInfos, CMSUtils.getProvider(provider));
}
/**
* @deprecated
*/
protected OutputStream open(
OutputStream out,
String macOID,
SecretKey encKey,
AlgorithmParameterSpec params,
ASN1EncodableVector recipientInfos,
Provider provider)
throws NoSuchAlgorithmException, CMSException
{
try
{
//
// ContentInfo
//
BERSequenceGenerator cGen = new BERSequenceGenerator(out);
cGen.addObject(CMSObjectIdentifiers.authenticatedData);
//
// Authenticated Data
//
BERSequenceGenerator authGen = new BERSequenceGenerator(cGen.getRawOutputStream(), 0, true);
authGen.addObject(new DERInteger(AuthenticatedData.calculateVersion(null)));
if (berEncodeRecipientSet)
{
authGen.getRawOutputStream().write(new BERSet(recipientInfos).getEncoded());
}
else
{
authGen.getRawOutputStream().write(new DERSet(recipientInfos).getEncoded());
}
Mac mac = CMSEnvelopedHelper.INSTANCE.getMac(macOID, provider);
mac.init(encKey, params);
AlgorithmIdentifier macAlgId = getAlgorithmIdentifier(macOID, params, provider);
authGen.getRawOutputStream().write(macAlgId.getEncoded());
BERSequenceGenerator eiGen = new BERSequenceGenerator(authGen.getRawOutputStream());
eiGen.addObject(CMSObjectIdentifiers.data);
OutputStream octetStream = CMSUtils.createBEROctetOutputStream(
eiGen.getRawOutputStream(), 0, false, bufferSize);
OutputStream mOut = new TeeOutputStream(octetStream, new MacOutputStream(mac));
return new OldCmsAuthenticatedDataOutputStream(mOut, mac, cGen, authGen, eiGen);
}
catch (InvalidKeyException e)
{
throw new CMSException("key invalid in message.", e);
}
catch (NoSuchPaddingException e)
{
throw new CMSException("required padding not supported.", e);
}
catch (InvalidAlgorithmParameterException e)
{
throw new CMSException("algorithm parameter invalid.", e);
}
catch (InvalidParameterSpecException e)
{
throw new CMSException("algorithm parameter spec invalid.", e);
}
catch (IOException e)
{
throw new CMSException("exception decoding algorithm parameters.", e);
}
}
/**
* generate an authenticated object that contains an CMS Authenticated Data
* object using the given provider.
* @throws java.io.IOException
* @deprecated use open(out, MacCalculator)
*/
public OutputStream open(
OutputStream out,
String encryptionOID,
String provider)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
{
return open(out, encryptionOID, CMSUtils.getProvider(provider));
}
/**
* @deprecated use open(out, MacCalculator)
*/
public OutputStream open(
OutputStream out,
String encryptionOID,
Provider provider)
throws NoSuchAlgorithmException, CMSException, IOException
{
KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
keyGen.init(rand);
return open(out, encryptionOID, keyGen, provider);
}
/**
* generate an enveloped object that contains an CMS Enveloped Data
* object using the given provider.
* @deprecated use open(out, MacCalculator)
*/
public OutputStream open(
OutputStream out,
String encryptionOID,
int keySize,
String provider)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
{
return open(out, encryptionOID, keySize, CMSUtils.getProvider(provider));
}
/**
* generate an enveloped object that contains an CMS Enveloped Data
* object using the given provider.
* @deprecated use open(out, MacCalculator)
*/
public OutputStream open(
OutputStream out,
String encryptionOID,
int keySize,
Provider provider)
throws NoSuchAlgorithmException, CMSException, IOException
{
KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
keyGen.init(keySize, rand);
return open(out, encryptionOID, keyGen, provider);
}
}