using System;
using System.Collections.Generic;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
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, CMSEnvelopedGenerator.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 random)
: base(random)
{
}
/// 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(recipientInfoGenerators.Count);
foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
{
try
{
recipientInfos.Add(rig.Generate(encKey, m_random));
}
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();
using (var recipGen = _berEncodeRecipientSet
? (Asn1Generator)new BerSetGenerator(envRaw)
: new DerSetGenerator(envRaw))
{
foreach (Asn1Encodable ae in recipientInfos)
{
recipGen.AddObject(ae);
}
}
BerSequenceGenerator eiGen = new BerSequenceGenerator(envRaw);
eiGen.AddObject(CmsObjectIdentifiers.Data);
eiGen.AddObject(encAlgID);
BerOctetStringGenerator octGen = new BerOctetStringGenerator(eiGen.GetRawOutputStream(), 0, false);
Stream octetOutputStream = octGen.GetOctetOutputStream(_bufferSize);
IBufferedCipher cipher = CipherUtilities.GetCipher(encAlgID.Algorithm);
cipher.Init(true, new ParametersWithRandom(cipherParameters, m_random));
CipherStream cOut = new CipherStream(octetOutputStream, null, cipher);
return new CmsEnvelopedDataOutputStream(this, cOut, cGen, envGen, eiGen, octGen);
}
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(m_random, 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(m_random, 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;
private readonly BerOctetStringGenerator _octGen;
public CmsEnvelopedDataOutputStream(
CmsEnvelopedGenerator outer,
CipherStream outStream,
BerSequenceGenerator cGen,
BerSequenceGenerator envGen,
BerSequenceGenerator eiGen,
BerOctetStringGenerator octGen)
{
_outer = outer;
_out = outStream;
_cGen = cGen;
_envGen = envGen;
_eiGen = eiGen;
_octGen = octGen;
}
public override void Write(byte[] buffer, int offset, int count)
{
_out.Write(buffer, offset, count);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public override void Write(ReadOnlySpan buffer)
{
_out.Write(buffer);
}
#endif
public override void WriteByte(byte value)
{
_out.WriteByte(value);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_out.Dispose();
// TODO Parent context(s) should really be closed explicitly
_octGen.Dispose();
_eiGen.Dispose();
if (_outer.unprotectedAttributeGenerator != null)
{
Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(
new Dictionary());
Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector());
_envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs));
}
_envGen.Dispose();
_cGen.Dispose();
}
base.Dispose(disposing);
}
}
}
}