using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a CMS enveloped-data message stream.
*
* A simple example of usage.
*
* CmsEnvelopedDataStreamGenerator edGen = new CmsEnvelopedDataStreamGenerator();
*
* edGen.AddKeyTransRecipient(cert);
*
* MemoryStream bOut = new MemoryStream();
*
* Stream out = edGen.Open(
* bOut, CMSEnvelopedDataGenerator.AES128_CBC);*
* out.Write(data);
*
* out.Close();
*
*
*/
public class CmsEnvelopedDataStreamGenerator
: CmsEnvelopedGenerator
{
private object _originatorInfo = null;
private object _unprotectedAttributes = null;
private int _bufferSize;
private bool _berEncodeRecipientSet;
public CmsEnvelopedDataStreamGenerator()
{
}
/// Constructor allowing specific source of randomness
/// Instance of SecureRandom to use.
public CmsEnvelopedDataStreamGenerator(
SecureRandom rand)
: base(rand)
{
}
/// Set the underlying string size for encapsulated data.
/// Length of octet strings to buffer the data.
public void SetBufferSize(
int bufferSize)
{
_bufferSize = bufferSize;
}
/// Use a BER Set to store the recipient information.
public void SetBerEncodeRecipients(
bool berEncodeRecipientSet)
{
_berEncodeRecipientSet = berEncodeRecipientSet;
}
private DerInteger Version
{
get
{
int version = (_originatorInfo != null || _unprotectedAttributes != null)
? 2
: 0;
return new DerInteger(version);
}
}
///
/// Generate an enveloped object that contains an CMS Enveloped Data
/// object using the passed in key generator.
///
private Stream Open(
Stream outStream,
string encryptionOid,
CipherKeyGenerator keyGen)
{
byte[] encKeyBytes = keyGen.GenerateKey();
KeyParameter encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes);
Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes);
ICipherParameters cipherParameters;
AlgorithmIdentifier encAlgID = GetAlgorithmIdentifier(
encryptionOid, encKey, asn1Params, out cipherParameters);
Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
{
try
{
recipientInfos.Add(rig.Generate(encKey, rand));
}
catch (InvalidKeyException e)
{
throw new CmsException("key inappropriate for algorithm.", e);
}
catch (GeneralSecurityException e)
{
throw new CmsException("error making encrypted content.", e);
}
}
return Open(outStream, encAlgID, cipherParameters, recipientInfos);
}
private Stream Open(
Stream outStream,
AlgorithmIdentifier encAlgID,
ICipherParameters cipherParameters,
Asn1EncodableVector recipientInfos)
{
try
{
//
// ContentInfo
//
BerSequenceGenerator cGen = new BerSequenceGenerator(outStream);
cGen.AddObject(CmsObjectIdentifiers.EnvelopedData);
//
// Encrypted Data
//
BerSequenceGenerator envGen = new BerSequenceGenerator(
cGen.GetRawOutputStream(), 0, true);
envGen.AddObject(this.Version);
Stream envRaw = envGen.GetRawOutputStream();
Asn1Generator recipGen = _berEncodeRecipientSet
? (Asn1Generator) new BerSetGenerator(envRaw)
: new DerSetGenerator(envRaw);
foreach (Asn1Encodable ae in recipientInfos)
{
recipGen.AddObject(ae);
}
recipGen.Close();
BerSequenceGenerator eiGen = new BerSequenceGenerator(envRaw);
eiGen.AddObject(CmsObjectIdentifiers.Data);
eiGen.AddObject(encAlgID);
Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream(
eiGen.GetRawOutputStream(), 0, false, _bufferSize);
IBufferedCipher cipher = CipherUtilities.GetCipher(encAlgID.ObjectID);
cipher.Init(true, new ParametersWithRandom(cipherParameters, rand));
CipherStream cOut = new CipherStream(octetOutputStream, null, cipher);
return new CmsEnvelopedDataOutputStream(this, cOut, cGen, envGen, eiGen);
}
catch (SecurityUtilityException e)
{
throw new CmsException("couldn't create cipher.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key invalid in message.", e);
}
catch (IOException e)
{
throw new CmsException("exception decoding algorithm parameters.", e);
}
}
/**
* generate an enveloped object that contains an CMS Enveloped Data object
* @throws IOException
*/
public Stream Open(
Stream outStream,
string encryptionOid)
{
CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
return Open(outStream, encryptionOid, keyGen);
}
/**
* generate an enveloped object that contains an CMS Enveloped Data object
* @throws IOException
*/
public Stream Open(
Stream outStream,
string encryptionOid,
int keySize)
{
CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
keyGen.Init(new KeyGenerationParameters(rand, keySize));
return Open(outStream, encryptionOid, keyGen);
}
private class CmsEnvelopedDataOutputStream
: BaseOutputStream
{
private readonly CmsEnvelopedGenerator _outer;
private readonly CipherStream _out;
private readonly BerSequenceGenerator _cGen;
private readonly BerSequenceGenerator _envGen;
private readonly BerSequenceGenerator _eiGen;
public CmsEnvelopedDataOutputStream(
CmsEnvelopedGenerator outer,
CipherStream outStream,
BerSequenceGenerator cGen,
BerSequenceGenerator envGen,
BerSequenceGenerator eiGen)
{
_outer = outer;
_out = outStream;
_cGen = cGen;
_envGen = envGen;
_eiGen = eiGen;
}
public override void WriteByte(
byte b)
{
_out.WriteByte(b);
}
public override void Write(
byte[] bytes,
int off,
int len)
{
_out.Write(bytes, off, len);
}
public override void Close()
{
_out.Close();
// TODO Parent context(s) should really be be closed explicitly
_eiGen.Close();
if (_outer.unprotectedAttributeGenerator != null)
{
Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable());
Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector());
_envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs));
}
_envGen.Close();
_cGen.Close();
base.Close();
}
}
}
}