Initial Commit

This commit is contained in:
2023-06-21 12:46:23 -04:00
commit c70248a520
1352 changed files with 336780 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
using System;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Cms
{
internal class BaseDigestCalculator
: IDigestCalculator
{
private readonly byte[] digest;
internal BaseDigestCalculator(
byte[] digest)
{
this.digest = digest;
}
public byte[] GetDigest()
{
return Arrays.Clone(digest);
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Org.BouncyCastle.Cms
{
public class CmsAttributeTableGenerationException
: CmsException
{
public CmsAttributeTableGenerationException()
{
}
public CmsAttributeTableGenerationException(
string name)
: base(name)
{
}
public CmsAttributeTableGenerationException(
string name,
Exception e)
: base(name, e)
{
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1.Cms;
namespace Org.BouncyCastle.Cms
{
/// <remarks>
/// The 'Signature' parameter is only available when generating unsigned attributes.
/// </remarks>
public enum CmsAttributeTableParameter
{
// const string ContentType = "contentType";
// const string Digest = "digest";
// const string Signature = "encryptedDigest";
// const string DigestAlgorithmIdentifier = "digestAlgID";
ContentType, Digest, Signature, DigestAlgorithmIdentifier
}
public interface CmsAttributeTableGenerator
{
AttributeTable GetAttributes(IDictionary parameters);
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Cms
{
/**
* containing class for an CMS Compressed Data object
*/
public class CmsCompressedData
{
internal ContentInfo contentInfo;
public CmsCompressedData(
byte[] compressedData)
: this(CmsUtilities.ReadContentInfo(compressedData))
{
}
public CmsCompressedData(
Stream compressedDataStream)
: this(CmsUtilities.ReadContentInfo(compressedDataStream))
{
}
public CmsCompressedData(
ContentInfo contentInfo)
{
this.contentInfo = contentInfo;
}
public byte[] GetContent()
{
CompressedData comData = CompressedData.GetInstance(contentInfo.Content);
ContentInfo content = comData.EncapContentInfo;
Asn1OctetString bytes = (Asn1OctetString) content.Content;
ZInflaterInputStream zIn = new ZInflaterInputStream(bytes.GetOctetStream());
try
{
return CmsUtilities.StreamToByteArray(zIn);
}
catch (IOException e)
{
throw new CmsException("exception reading compressed stream.", e);
}
finally
{
zIn.Close();
}
}
/**
* return the ContentInfo
*/
public ContentInfo ContentInfo
{
get { return contentInfo; }
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] GetEncoded()
{
return contentInfo.GetEncoded();
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a compressed CMS message.
* <p>
* A simple example of usage.</p>
* <p>
* <pre>
* CMSCompressedDataGenerator fact = new CMSCompressedDataGenerator();
* CMSCompressedData data = fact.Generate(content, algorithm);
* </pre>
* </p>
*/
public class CmsCompressedDataGenerator
{
public const string ZLib = "1.2.840.113549.1.9.16.3.8";
public CmsCompressedDataGenerator()
{
}
/**
* Generate an object that contains an CMS Compressed Data
*/
public CmsCompressedData Generate(
CmsProcessable content,
string compressionOid)
{
AlgorithmIdentifier comAlgId;
Asn1OctetString comOcts;
try
{
MemoryStream bOut = new MemoryStream();
ZDeflaterOutputStream zOut = new ZDeflaterOutputStream(bOut);
content.Write(zOut);
zOut.Close();
comAlgId = new AlgorithmIdentifier(
new DerObjectIdentifier(compressionOid),
null);
comOcts = new BerOctetString(bOut.ToArray());
}
catch (IOException e)
{
throw new CmsException("exception encoding data.", e);
}
ContentInfo comContent = new ContentInfo(CmsObjectIdentifiers.Data, comOcts);
ContentInfo contentInfo = new ContentInfo(
CmsObjectIdentifiers.CompressedData,
new CompressedData(comAlgId, comContent));
return new CmsCompressedData(contentInfo);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Cms
{
/**
* Class for reading a CMS Compressed Data stream.
* <pre>
* CMSCompressedDataParser cp = new CMSCompressedDataParser(inputStream);
*
* process(cp.GetContent().GetContentStream());
* </pre>
* Note: this class does not introduce buffering - if you are processing large files you should create
* the parser with:
* <pre>
* CMSCompressedDataParser ep = new CMSCompressedDataParser(new BufferedInputStream(inputStream, bufSize));
* </pre>
* where bufSize is a suitably large buffer size.
*/
public class CmsCompressedDataParser
: CmsContentInfoParser
{
public CmsCompressedDataParser(
byte[] compressedData)
: this(new MemoryStream(compressedData, false))
{
}
public CmsCompressedDataParser(
Stream compressedData)
: base(compressedData)
{
}
public CmsTypedStream GetContent()
{
try
{
CompressedDataParser comData = new CompressedDataParser((Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence));
ContentInfoParser content = comData.GetEncapContentInfo();
Asn1OctetStringParser bytes = (Asn1OctetStringParser)content.GetContent(Asn1Tags.OctetString);
return new CmsTypedStream(content.ContentType.ToString(), new ZInflaterInputStream(bytes.GetOctetStream()));
}
catch (IOException e)
{
throw new CmsException("IOException reading compressed content.", e);
}
}
}
}

View File

@@ -0,0 +1,124 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a compressed CMS message stream.
* <p>
* A simple example of usage.
* </p>
* <pre>
* CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator();
*
* Stream cOut = gen.Open(outputStream, CMSCompressedDataStreamGenerator.ZLIB);
*
* cOut.Write(data);
*
* cOut.Close();
* </pre>
*/
public class CmsCompressedDataStreamGenerator
{
public const string ZLib = "1.2.840.113549.1.9.16.3.8";
/**
* base constructor
*/
public CmsCompressedDataStreamGenerator()
{
}
public Stream Open(
Stream outStream,
string compressionOID)
{
return Open(outStream, CmsObjectIdentifiers.Data.Id, compressionOID);
}
public Stream Open(
Stream outStream,
string contentOID,
string compressionOID)
{
BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
sGen.AddObject(CmsObjectIdentifiers.CompressedData);
//
// Compressed Data
//
BerSequenceGenerator cGen = new BerSequenceGenerator(
sGen.GetRawOutputStream(), 0, true);
// CMSVersion
cGen.AddObject(new DerInteger(0));
// CompressionAlgorithmIdentifier
cGen.AddObject(new AlgorithmIdentifier(new DerObjectIdentifier(ZLib)));
//
// Encapsulated ContentInfo
//
BerSequenceGenerator eiGen = new BerSequenceGenerator(cGen.GetRawOutputStream());
eiGen.AddObject(new DerObjectIdentifier(contentOID));
BerOctetStringGenerator octGen = new BerOctetStringGenerator(
eiGen.GetRawOutputStream(), 0, true);
return new CmsCompressedOutputStream(
new ZDeflaterOutputStream(octGen.GetOctetOutputStream()), sGen, cGen, eiGen);
}
private class CmsCompressedOutputStream
: BaseOutputStream
{
private ZDeflaterOutputStream _out;
private BerSequenceGenerator _sGen;
private BerSequenceGenerator _cGen;
private BerSequenceGenerator _eiGen;
internal CmsCompressedOutputStream(
ZDeflaterOutputStream outStream,
BerSequenceGenerator sGen,
BerSequenceGenerator cGen,
BerSequenceGenerator eiGen)
{
_out = outStream;
_sGen = sGen;
_cGen = cGen;
_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();
_eiGen.Close();
_cGen.Close();
_sGen.Close();
base.Close();
}
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
namespace Org.BouncyCastle.Cms
{
public class CmsContentInfoParser
{
protected ContentInfoParser contentInfo;
protected Stream data;
protected CmsContentInfoParser(
Stream data)
{
if (data == null)
throw new ArgumentNullException("data");
this.data = data;
try
{
Asn1StreamParser inStream = new Asn1StreamParser(data, CmsUtilities.MaximumMemory);
this.contentInfo = new ContentInfoParser((Asn1SequenceParser)inStream.ReadObject());
}
catch (IOException e)
{
throw new CmsException("IOException reading content.", e);
}
catch (InvalidCastException e)
{
throw new CmsException("Unexpected object reading content.", e);
}
}
/**
* Close the underlying data stream.
* @throws IOException if the close fails.
*/
public void Close()
{
this.data.Close();
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Cms
{
/**
* containing class for an CMS Enveloped Data object
*/
public class CmsEnvelopedData
{
internal RecipientInformationStore recipientInfoStore;
internal ContentInfo contentInfo;
private AlgorithmIdentifier encAlg;
private Asn1Set unprotectedAttributes;
public CmsEnvelopedData(
byte[] envelopedData)
: this(CmsUtilities.ReadContentInfo(envelopedData))
{
}
public CmsEnvelopedData(
Stream envelopedData)
: this(CmsUtilities.ReadContentInfo(envelopedData))
{
}
public CmsEnvelopedData(
ContentInfo contentInfo)
{
this.contentInfo = contentInfo;
EnvelopedData envData = EnvelopedData.GetInstance(contentInfo.Content);
//
// read the encrypted content info
//
EncryptedContentInfo encInfo = envData.EncryptedContentInfo;
this.encAlg = encInfo.ContentEncryptionAlgorithm;
//
// load the RecipientInfoStore
//
Asn1Set s = envData.RecipientInfos;
IList infos = new ArrayList();
byte[] contentOctets = encInfo.EncryptedContent.GetOctets();
foreach (Asn1Encodable ae in s)
{
RecipientInfo info = RecipientInfo.GetInstance(ae);
MemoryStream contentStream = new MemoryStream(contentOctets, false);
object type = info.Info;
if (type is KeyTransRecipientInfo)
{
infos.Add(new KeyTransRecipientInformation(
(KeyTransRecipientInfo) type, encAlg, contentStream));
}
else if (type is KekRecipientInfo)
{
infos.Add(new KekRecipientInformation(
(KekRecipientInfo) type, encAlg, contentStream));
}
else if (type is KeyAgreeRecipientInfo)
{
infos.Add(new KeyAgreeRecipientInformation(
(KeyAgreeRecipientInfo) type, encAlg, contentStream));
}
else if (type is PasswordRecipientInfo)
{
infos.Add(new PasswordRecipientInformation(
(PasswordRecipientInfo) type, encAlg, contentStream));
}
}
this.recipientInfoStore = new RecipientInformationStore(infos);
this.unprotectedAttributes = envData.UnprotectedAttrs;
}
public AlgorithmIdentifier EncryptionAlgorithmID
{
get { return encAlg; }
}
/**
* return the object identifier for the content encryption algorithm.
*/
public string EncryptionAlgOid
{
get { return encAlg.ObjectID.Id; }
}
/**
* return a store of the intended recipients for this message
*/
public RecipientInformationStore GetRecipientInfos()
{
return recipientInfoStore;
}
/**
* return the ContentInfo
*/
public ContentInfo ContentInfo
{
get { return contentInfo; }
}
/**
* return a table of the unprotected attributes indexed by
* the OID of the attribute.
*/
public Asn1.Cms.AttributeTable GetUnprotectedAttributes()
{
if (unprotectedAttributes == null)
return null;
return new Asn1.Cms.AttributeTable(unprotectedAttributes);
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] GetEncoded()
{
return contentInfo.GetEncoded();
}
}
}

View File

@@ -0,0 +1,174 @@
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.Oiw;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Date;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/// <remarks>
/// General class for generating a CMS enveloped-data message.
///
/// A simple example of usage.
///
/// <pre>
/// CmsEnvelopedDataGenerator fact = new CmsEnvelopedDataGenerator();
///
/// fact.AddKeyTransRecipient(cert);
///
/// CmsEnvelopedData data = fact.Generate(content, algorithm);
/// </pre>
/// </remarks>
public class CmsEnvelopedDataGenerator
: CmsEnvelopedGenerator
{
public CmsEnvelopedDataGenerator()
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
public CmsEnvelopedDataGenerator(
SecureRandom rand)
: base(rand)
{
}
/// <summary>
/// Generate an enveloped object that contains a CMS Enveloped Data
/// object using the passed in key generator.
/// </summary>
private CmsEnvelopedData Generate(
CmsProcessable content,
string encryptionOid,
CipherKeyGenerator keyGen)
{
AlgorithmIdentifier encAlgId = null;
KeyParameter encKey;
Asn1OctetString encContent;
try
{
byte[] encKeyBytes = keyGen.GenerateKey();
encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes);
Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes);
ICipherParameters cipherParameters;
encAlgId = GetAlgorithmIdentifier(
encryptionOid, encKey, asn1Params, out cipherParameters);
IBufferedCipher cipher = CipherUtilities.GetCipher(encryptionOid);
cipher.Init(true, new ParametersWithRandom(cipherParameters, rand));
MemoryStream bOut = new MemoryStream();
CipherStream cOut = new CipherStream(bOut, null, cipher);
content.Write(cOut);
cOut.Close();
encContent = new BerOctetString(bOut.ToArray());
}
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);
}
Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
foreach (RecipientInf recipient in recipientInfs)
{
try
{
recipientInfos.Add(recipient.ToRecipientInfo(encKey, rand));
}
catch (IOException e)
{
throw new CmsException("encoding error.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key inappropriate for algorithm.", e);
}
catch (GeneralSecurityException e)
{
throw new CmsException("error making encrypted content.", e);
}
}
EncryptedContentInfo eci = new EncryptedContentInfo(
PkcsObjectIdentifiers.Data,
encAlgId,
encContent);
Asn1.Cms.ContentInfo contentInfo = new Asn1.Cms.ContentInfo(
PkcsObjectIdentifiers.EnvelopedData,
new EnvelopedData(null, new DerSet(recipientInfos), eci, null));
return new CmsEnvelopedData(contentInfo);
}
/// <summary>Generate an enveloped object that contains an CMS Enveloped Data object.</summary>
public CmsEnvelopedData Generate(
CmsProcessable content,
string encryptionOid)
{
try
{
CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
return Generate(content, encryptionOid, keyGen);
}
catch (SecurityUtilityException e)
{
throw new CmsException("can't find key generation algorithm.", e);
}
}
/// <summary>Generate an enveloped object that contains an CMS Enveloped Data object.</summary>
public CmsEnvelopedData Generate(
CmsProcessable content,
string encryptionOid,
int keySize)
{
try
{
CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
keyGen.Init(new KeyGenerationParameters(rand, keySize));
return Generate(content, encryptionOid, keyGen);
}
catch (SecurityUtilityException e)
{
throw new CmsException("can't find key generation algorithm.", e);
}
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
namespace Org.BouncyCastle.Cms
{
/**
* Parsing class for an CMS Enveloped Data object from an input stream.
* <p>
* Note: that because we are in a streaming mode only one recipient can be tried and it is important
* that the methods on the parser are called in the appropriate order.
* </p>
* <p>
* Example of use - assuming the first recipient matches the private key we have.
* <pre>
* CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(inputStream);
*
* RecipientInformationStore recipients = ep.GetRecipientInfos();
*
* Collection c = recipients.getRecipients();
* Iterator it = c.iterator();
*
* if (it.hasNext())
* {
* RecipientInformation recipient = (RecipientInformation)it.next();
*
* CMSTypedStream recData = recipient.getContentStream(privateKey);
*
* processDataStream(recData.getContentStream());
* }
* </pre>
* Note: this class does not introduce buffering - if you are processing large files you should create
* the parser with:
* <pre>
* CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(new BufferedInputStream(inputStream, bufSize));
* </pre>
* where bufSize is a suitably large buffer size.
* </p>
*/
public class CmsEnvelopedDataParser
: CmsContentInfoParser
{
internal RecipientInformationStore recipientInfoStore;
internal EnvelopedDataParser envelopedData;
private AlgorithmIdentifier _encAlg;
private Asn1.Cms.AttributeTable _unprotectedAttributes;
private bool _attrNotRead;
public CmsEnvelopedDataParser(
byte[] envelopedData)
: this(new MemoryStream(envelopedData, false))
{
}
public CmsEnvelopedDataParser(
Stream envelopedData)
: base(envelopedData)
{
this._attrNotRead = true;
this.envelopedData = new EnvelopedDataParser(
(Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence));
//
// load the RecipientInfoStore
//
Asn1SetParser s = this.envelopedData.GetRecipientInfos();
IList baseInfos = new ArrayList();
IAsn1Convertible entry;
while ((entry = s.ReadObject()) != null)
{
baseInfos.Add(RecipientInfo.GetInstance(entry.ToAsn1Object()));
}
//
// read the encrypted content info
//
EncryptedContentInfoParser encInfo = this.envelopedData.GetEncryptedContentInfo();
this._encAlg = encInfo.ContentEncryptionAlgorithm;
//
// prime the recipients
//
IList infos = new ArrayList();
Stream dataStream = ((Asn1OctetStringParser)encInfo.GetEncryptedContent(Asn1Tags.OctetString)).GetOctetStream();
foreach (Asn1.Cms.RecipientInfo info in baseInfos)
{
Asn1Encodable recipInfo = info.Info;
if (recipInfo is Asn1.Cms.KeyTransRecipientInfo)
{
infos.Add(new KeyTransRecipientInformation(
(KeyTransRecipientInfo) recipInfo, _encAlg, dataStream));
}
else if (recipInfo is Asn1.Cms.KekRecipientInfo)
{
infos.Add(new KekRecipientInformation(
(KekRecipientInfo) recipInfo, _encAlg, dataStream));
}
else if (recipInfo is KeyAgreeRecipientInfo)
{
infos.Add(new KeyAgreeRecipientInformation(
(KeyAgreeRecipientInfo) recipInfo, _encAlg, dataStream));
}
else if (recipInfo is PasswordRecipientInfo)
{
infos.Add(new PasswordRecipientInformation(
(PasswordRecipientInfo) recipInfo, _encAlg, dataStream));
}
}
this.recipientInfoStore = new RecipientInformationStore(infos);
}
public AlgorithmIdentifier EncryptionAlgorithmID
{
get { return _encAlg; }
}
/**
* return the object identifier for the content encryption algorithm.
*/
public string EncryptionAlgOid
{
get { return _encAlg.ObjectID.Id; }
}
/**
* return the ASN.1 encoded encryption algorithm parameters, or null if
* there aren't any.
*/
public Asn1Object EncryptionAlgParams
{
get
{
Asn1Encodable ae = _encAlg.Parameters;
return ae == null ? null : ae.ToAsn1Object();
}
}
/**
* return a store of the intended recipients for this message
*/
public RecipientInformationStore GetRecipientInfos()
{
return this.recipientInfoStore;
}
/**
* return a table of the unprotected attributes indexed by
* the OID of the attribute.
* @throws IOException
*/
public Asn1.Cms.AttributeTable GetUnprotectedAttributes()
{
if (_unprotectedAttributes == null && _attrNotRead)
{
Asn1SetParser asn1Set = this.envelopedData.GetUnprotectedAttrs();
_attrNotRead = false;
if (asn1Set != null)
{
Asn1EncodableVector v = new Asn1EncodableVector();
IAsn1Convertible o;
while ((o = asn1Set.ReadObject()) != null)
{
Asn1SequenceParser seq = (Asn1SequenceParser)o;
v.Add(seq.ToAsn1Object());
}
_unprotectedAttributes = new Asn1.Cms.AttributeTable(new DerSet(v));
}
}
return _unprotectedAttributes;
}
}
}

View File

@@ -0,0 +1,273 @@
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.Pkcs;
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.IO;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a CMS enveloped-data message stream.
* <p>
* A simple example of usage.
* <pre>
* CmsEnvelopedDataStreamGenerator edGen = new CmsEnvelopedDataStreamGenerator();
*
* edGen.AddKeyTransRecipient(cert);
*
* MemoryStream bOut = new MemoryStream();
*
* Stream out = edGen.Open(
* bOut, CMSEnvelopedDataGenerator.AES128_CBC);*
* out.Write(data);
*
* out.Close();
* </pre>
* </p>
*/
public class CmsEnvelopedDataStreamGenerator
: CmsEnvelopedGenerator
{
private object _originatorInfo = null;
private object _unprotectedAttributes = null;
private int _bufferSize;
private bool _berEncodeRecipientSet;
public CmsEnvelopedDataStreamGenerator()
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
public CmsEnvelopedDataStreamGenerator(
SecureRandom rand)
: base(rand)
{
}
/// <summary>Set the underlying string size for encapsulated data.</summary>
/// <param name="bufferSize">Length of octet strings to buffer the data.</param>
public void SetBufferSize(
int bufferSize)
{
_bufferSize = bufferSize;
}
/// <summary>Use a BER Set to store the recipient information.</summary>
public void SetBerEncodeRecipients(
bool berEncodeRecipientSet)
{
_berEncodeRecipientSet = berEncodeRecipientSet;
}
private DerInteger Version
{
get
{
int version = (_originatorInfo != null || _unprotectedAttributes != null)
? 2
: 0;
return new DerInteger(version);
}
}
/// <summary>
/// Generate an enveloped object that contains an CMS Enveloped Data
/// object using the passed in key generator.
/// </summary>
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 (RecipientInf recipient in recipientInfs)
{
try
{
recipientInfos.Add(recipient.ToRecipientInfo(encKey, rand));
}
catch (IOException e)
{
throw new CmsException("encoding error.", e);
}
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);
Asn1Generator recipGen = _berEncodeRecipientSet
? (Asn1Generator) new BerSetGenerator(envGen.GetRawOutputStream())
: new DerSetGenerator(envGen.GetRawOutputStream());
foreach (Asn1Encodable ae in recipientInfos)
{
recipGen.AddObject(ae);
}
recipGen.Close();
BerSequenceGenerator eiGen = new BerSequenceGenerator(
envGen.GetRawOutputStream());
eiGen.AddObject(PkcsObjectIdentifiers.Data);
eiGen.AddObject(encAlgID);
BerOctetStringGenerator octGen = new BerOctetStringGenerator(
eiGen.GetRawOutputStream(), 0, false);
Stream octetOutputStream = octGen.GetOctetOutputStream(_bufferSize);
IBufferedCipher cipher = CipherUtilities.GetCipher(encAlgID.ObjectID);
cipher.Init(true, new ParametersWithRandom(cipherParameters, rand));
CipherStream cOut = new CipherStream(octetOutputStream, null, cipher);
return new CmsEnvelopedDataOutputStream(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 CipherStream _out;
private BerSequenceGenerator _cGen;
private BerSequenceGenerator _envGen;
private BerSequenceGenerator _eiGen;
public CmsEnvelopedDataOutputStream(
CipherStream outStream,
BerSequenceGenerator cGen,
BerSequenceGenerator envGen,
BerSequenceGenerator eiGen)
{
_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();
_eiGen.Close();
// [TODO] unprotected attributes go here
_envGen.Close();
_cGen.Close();
base.Close();
}
}
}
}

View File

@@ -0,0 +1,551 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.Kisa;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Ntt;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a CMS enveloped-data message.
*
* A simple example of usage.
*
* <pre>
* CMSEnvelopedDataGenerator fact = new CMSEnvelopedDataGenerator();
*
* fact.addKeyTransRecipient(cert);
*
* CMSEnvelopedData data = fact.generate(content, algorithm, "BC");
* </pre>
*/
public class CmsEnvelopedGenerator
{
internal static readonly short[] rc2Table =
{
0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
};
internal static readonly short[] rc2Ekb =
{
0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd
};
// TODO Create named constants for all of these
public static readonly string DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc.Id;
public static readonly string RC2Cbc = PkcsObjectIdentifiers.RC2Cbc.Id;
public const string IdeaCbc = "1.3.6.1.4.1.188.7.1.1.2";
public const string Cast5Cbc = "1.2.840.113533.7.66.10";
public static readonly string Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc.Id;
public static readonly string Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc.Id;
public static readonly string Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc.Id;
public static readonly string Camellia128Cbc = NttObjectIdentifiers.IdCamellia128Cbc.Id;
public static readonly string Camellia192Cbc = NttObjectIdentifiers.IdCamellia192Cbc.Id;
public static readonly string Camellia256Cbc = NttObjectIdentifiers.IdCamellia256Cbc.Id;
public static readonly string SeedCbc = KisaObjectIdentifiers.IdSeedCbc.Id;
public static readonly string DesEde3Wrap = PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id;
public static readonly string Aes128Wrap = NistObjectIdentifiers.IdAes128Wrap.Id;
public static readonly string Aes192Wrap = NistObjectIdentifiers.IdAes192Wrap.Id;
public static readonly string Aes256Wrap = NistObjectIdentifiers.IdAes256Wrap.Id;
public static readonly string Camellia128Wrap = NttObjectIdentifiers.IdCamellia128Wrap.Id;
public static readonly string Camellia192Wrap = NttObjectIdentifiers.IdCamellia192Wrap.Id;
public static readonly string Camellia256Wrap = NttObjectIdentifiers.IdCamellia256Wrap.Id;
public static readonly string SeedWrap = KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap.Id;
public static readonly string ECDHSha1Kdf = X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id;
internal static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
internal readonly IList recipientInfs = new ArrayList();
internal readonly SecureRandom rand;
protected class RecipientInf
{
private readonly X509Certificate cert;
private AlgorithmIdentifier keyEncAlg;
private readonly AsymmetricKeyParameter pubKey;
private readonly Asn1OctetString subKeyId;
private readonly string secKeyAlgorithm;
private readonly KeyParameter secKey;
private readonly KekIdentifier secKeyId;
private readonly OriginatorIdentifierOrKey originator;
private const Asn1OctetString ukm = null;
private readonly AlgorithmIdentifier derivationAlg;
internal RecipientInf(
X509Certificate cert)
{
this.cert = cert;
this.pubKey = cert.GetPublicKey();
try
{
TbsCertificateStructure tbs = TbsCertificateStructure.GetInstance(
Asn1Object.FromByteArray(cert.GetTbsCertificate()));
keyEncAlg = tbs.SubjectPublicKeyInfo.AlgorithmID;
}
// catch (IOException e)
catch (Exception)
{
throw new ArgumentException("can't extract key algorithm from this cert");
}
// catch (CertificateEncodingException)
// {
// throw new ArgumentException("can't extract tbs structure from this cert");
// }
}
internal RecipientInf(
AsymmetricKeyParameter pubKey,
Asn1OctetString subKeyId)
{
this.pubKey = pubKey;
this.subKeyId = subKeyId;
try
{
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
keyEncAlg = info.AlgorithmID;
}
catch (IOException)
{
throw new ArgumentException("can't extract key algorithm from this key");
}
}
internal RecipientInf(
string secKeyAlgorithm, // TODO Can get this from secKey?
KeyParameter secKey,
KekIdentifier secKeyId)
{
this.secKeyAlgorithm = secKeyAlgorithm;
this.secKey = secKey;
this.secKeyId = secKeyId;
if (secKeyAlgorithm.StartsWith("DES"))
{
keyEncAlg = new AlgorithmIdentifier(
PkcsObjectIdentifiers.IdAlgCms3DesWrap,
DerNull.Instance);
}
else if (secKeyAlgorithm.StartsWith("RC2"))
{
keyEncAlg = new AlgorithmIdentifier(
PkcsObjectIdentifiers.IdAlgCmsRC2Wrap,
new DerInteger(58));
}
else if (secKeyAlgorithm.StartsWith("AES"))
{
int length = secKey.GetKey().Length * 8;
DerObjectIdentifier wrapOid;
if (length == 128)
{
wrapOid = NistObjectIdentifiers.IdAes128Wrap;
}
else if (length == 192)
{
wrapOid = NistObjectIdentifiers.IdAes192Wrap;
}
else if (length == 256)
{
wrapOid = NistObjectIdentifiers.IdAes256Wrap;
}
else
{
throw new ArgumentException("illegal keysize in AES");
}
keyEncAlg = new AlgorithmIdentifier(wrapOid); // parameters absent
}
else if (secKeyAlgorithm.StartsWith("SEED"))
{
// parameters absent
keyEncAlg = new AlgorithmIdentifier(KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap);
}
else if (secKeyAlgorithm.StartsWith("CAMELLIA"))
{
int length = secKey.GetKey().Length * 8;
DerObjectIdentifier wrapOid;
if (length == 128)
{
wrapOid = NttObjectIdentifiers.IdCamellia128Wrap;
}
else if (length == 192)
{
wrapOid = NttObjectIdentifiers.IdCamellia192Wrap;
}
else if (length == 256)
{
wrapOid = NttObjectIdentifiers.IdCamellia256Wrap;
}
else
{
throw new ArgumentException("illegal keysize in Camellia");
}
keyEncAlg = new AlgorithmIdentifier(wrapOid); // parameters must be absent
}
else
{
throw new ArgumentException("unknown algorithm");
}
}
public RecipientInf(
string secKeyAlgorithm, // TODO Can get this from secKey?
KeyParameter secKey,
string algorithm,
string wrapOid,
OriginatorIdentifierOrKey originator,
X509Certificate cert)
{
DerSequence paramSeq = new DerSequence(
new DerObjectIdentifier(wrapOid),
DerNull.Instance);
this.secKeyAlgorithm = secKeyAlgorithm;
this.secKey = secKey;
this.keyEncAlg = new AlgorithmIdentifier(new DerObjectIdentifier(algorithm), paramSeq);
this.originator = originator;
this.cert = cert;
}
public RecipientInf(
string secKeyAlgorithm, // TODO Can get this from secKey?
KeyParameter secKey,
AlgorithmIdentifier derivationAlg)
{
this.secKeyAlgorithm = secKeyAlgorithm;
this.secKey = secKey;
this.derivationAlg = derivationAlg;
}
internal RecipientInfo ToRecipientInfo(
KeyParameter key,
SecureRandom random)
{
byte[] keyBytes = key.GetKey();
if (pubKey != null)
{
IWrapper keyWrapper = Helper.CreateWrapper(keyEncAlg.ObjectID.Id);
keyWrapper.Init(true, new ParametersWithRandom(pubKey, random));
Asn1OctetString encKey = new DerOctetString(
keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
RecipientIdentifier recipId;
if (cert != null)
{
TbsCertificateStructure tbs = TbsCertificateStructure.GetInstance(
Asn1Object.FromByteArray(cert.GetTbsCertificate()));
Asn1.Cms.IssuerAndSerialNumber encSid = new Asn1.Cms.IssuerAndSerialNumber(
tbs.Issuer, tbs.SerialNumber.Value);
recipId = new RecipientIdentifier(encSid);
}
else
{
recipId = new RecipientIdentifier(subKeyId);
}
return new RecipientInfo(new KeyTransRecipientInfo(recipId, keyEncAlg, encKey));
}
else if (originator != null)
{
IWrapper keyWrapper = Helper.CreateWrapper(
DerObjectIdentifier.GetInstance(
Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id);
keyWrapper.Init(true, new ParametersWithRandom(secKey, random));
Asn1OctetString encKey = new DerOctetString(
keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
RecipientEncryptedKey rKey = new RecipientEncryptedKey(
new KeyAgreeRecipientIdentifier(
new Asn1.Cms.IssuerAndSerialNumber(
PrincipalUtilities.GetIssuerX509Principal(cert),
cert.SerialNumber)),
encKey);
return new RecipientInfo(
new KeyAgreeRecipientInfo(originator, ukm, keyEncAlg, new DerSequence(rKey)));
}
else if (derivationAlg != null)
{
string rfc3211WrapperName = Helper.GetRfc3211WrapperName(secKeyAlgorithm);
IWrapper keyWrapper = Helper.CreateWrapper(rfc3211WrapperName);
// Note: In Java build, the IV is automatically generated in JCE layer
int ivLength = rfc3211WrapperName.StartsWith("DESEDE") ? 8 : 16;
byte[] iv = new byte[ivLength];
random.NextBytes(iv);
ICipherParameters parameters = new ParametersWithIV(secKey, iv);
keyWrapper.Init(true, new ParametersWithRandom(parameters, random));
Asn1OctetString encKey = new DerOctetString(
keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
// byte[] iv = keyWrapper.GetIV();
DerSequence seq = new DerSequence(
new DerObjectIdentifier(secKeyAlgorithm),
new DerOctetString(iv));
keyEncAlg = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdAlgPwriKek, seq);
return new RecipientInfo(new PasswordRecipientInfo(derivationAlg, keyEncAlg, encKey));
}
else
{
IWrapper keyWrapper = Helper.CreateWrapper(keyEncAlg.ObjectID.Id);
keyWrapper.Init(true, new ParametersWithRandom(secKey, random));
Asn1OctetString encKey = new DerOctetString(
keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
return new RecipientInfo(new KekRecipientInfo(secKeyId, keyEncAlg, encKey));
}
}
}
public CmsEnvelopedGenerator()
: this(new SecureRandom())
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
public CmsEnvelopedGenerator(
SecureRandom rand)
{
this.rand = rand;
}
/**
* add a recipient.
*
* @param cert recipient's public key certificate
* @exception ArgumentException if there is a problem with the certificate
*/
public void AddKeyTransRecipient(
X509Certificate cert)
{
recipientInfs.Add(new RecipientInf(cert));
}
/**
* add a recipient
*
* @param key the public key used by the recipient
* @param subKeyId the identifier for the recipient's public key
* @exception ArgumentException if there is a problem with the key
*/
public void AddKeyTransRecipient(
AsymmetricKeyParameter pubKey,
byte[] subKeyId)
{
recipientInfs.Add(new CmsEnvelopedGenerator.RecipientInf(pubKey, new DerOctetString(subKeyId)));
}
/**
* add a KEK recipient.
* @param key the secret key to use for wrapping
* @param keyIdentifier the byte string that identifies the key
*/
public void AddKekRecipient(
string keyAlgorithm, // TODO Remove need for this parameter
KeyParameter key,
byte[] keyIdentifier)
{
recipientInfs.Add(new RecipientInf(keyAlgorithm, key, new KekIdentifier(keyIdentifier, null, null)));
}
public void AddPasswordRecipient(
CmsPbeKey pbeKey,
string kekAlgorithmOid)
{
Pbkdf2Params p = new Pbkdf2Params(pbeKey.Salt, pbeKey.IterationCount);
KeyParameter secretKey = pbeKey.GetEncoded(kekAlgorithmOid);
recipientInfs.Add(new RecipientInf(kekAlgorithmOid, secretKey, new AlgorithmIdentifier(PkcsObjectIdentifiers.IdPbkdf2, p)));
}
/**
* Add a key agreement based recipient.
*
* @param agreementAlgorithm key agreement algorithm to use.
* @param senderPrivateKey private key to initialise sender side of agreement with.
* @param senderPublicKey sender public key to include with message.
* @param recipientCert recipient's public key certificate.
* @param cekWrapAlgorithm OID for key wrapping algorithm to use.
* @exception SecurityUtilityException if the algorithm requested cannot be found
* @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
*/
public void AddKeyAgreementRecipient(
string agreementAlgorithm,
AsymmetricKeyParameter senderPrivateKey,
AsymmetricKeyParameter senderPublicKey,
X509Certificate recipientCert,
string cekWrapAlgorithm)
{
if (!senderPrivateKey.IsPrivate)
throw new ArgumentException("Expected private key", "senderPrivateKey");
if (senderPublicKey.IsPrivate)
throw new ArgumentException("Expected public key", "senderPublicKey");
IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf(
agreementAlgorithm, cekWrapAlgorithm);
agreement.Init(new ParametersWithRandom(senderPrivateKey, rand));
BigInteger secretNum = agreement.CalculateAgreement(recipientCert.GetPublicKey());
try
{
SubjectPublicKeyInfo oPubKeyInfo =
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(senderPublicKey);
OriginatorIdentifierOrKey originator = new OriginatorIdentifierOrKey(
new OriginatorPublicKey(
new AlgorithmIdentifier(oPubKeyInfo.AlgorithmID.ObjectID, DerNull.Instance),
oPubKeyInfo.PublicKeyData.GetBytes()));
// TODO Fix the way bytes are derived from the secret
byte[] secretBytes = secretNum.ToByteArrayUnsigned();
KeyParameter secret = ParameterUtilities.CreateKeyParameter(
cekWrapAlgorithm, secretBytes);
recipientInfs.Add(
new RecipientInf(cekWrapAlgorithm, secret, agreementAlgorithm,
cekWrapAlgorithm, originator, recipientCert));
}
catch (IOException e)
{
throw new InvalidKeyException("cannot extract originator public key: " + e);
}
}
protected internal virtual AlgorithmIdentifier GetAlgorithmIdentifier(
string encryptionOid,
KeyParameter encKey,
Asn1Encodable asn1Params,
out ICipherParameters cipherParameters)
{
Asn1Object asn1Object;
if (asn1Params != null)
{
asn1Object = asn1Params.ToAsn1Object();
cipherParameters = ParameterUtilities.GetCipherParameters(
encryptionOid, encKey, asn1Object);
}
else
{
asn1Object = DerNull.Instance;
cipherParameters = encKey;
}
return new AlgorithmIdentifier(
new DerObjectIdentifier(encryptionOid),
asn1Object);
}
protected internal virtual Asn1Encodable GenerateAsn1Parameters(
string encryptionOid,
byte[] encKeyBytes)
{
Asn1Encodable asn1Params = null;
try
{
if (encryptionOid.Equals(RC2Cbc))
{
byte[] iv = new byte[8];
rand.NextBytes(iv);
// TODO Is this detailed repeat of Java version really necessary?
int effKeyBits = encKeyBytes.Length * 8;
int parameterVersion;
if (effKeyBits < 256)
{
parameterVersion = rc2Table[effKeyBits];
}
else
{
parameterVersion = effKeyBits;
}
asn1Params = new RC2CbcParameter(parameterVersion, iv);
}
else
{
asn1Params = ParameterUtilities.GenerateParameters(encryptionOid, rand);
}
}
catch (SecurityUtilityException)
{
// No problem... no parameters generated
}
return asn1Params;
}
}
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Cms
{
class CmsEnvelopedHelper
{
internal static readonly CmsEnvelopedHelper Instance = new CmsEnvelopedHelper();
private static readonly IDictionary KeySizes = new Hashtable();
private static readonly IDictionary Ciphers = new Hashtable();
private static readonly IDictionary BaseCipherNames = new Hashtable();
// private static readonly IDictionary CipherAlgNames = new Hashtable();
static CmsEnvelopedHelper()
{
KeySizes.Add(CmsEnvelopedGenerator.DesEde3Cbc, 192);
KeySizes.Add(CmsEnvelopedGenerator.Aes128Cbc, 128);
KeySizes.Add(CmsEnvelopedGenerator.Aes192Cbc, 192);
KeySizes.Add(CmsEnvelopedGenerator.Aes256Cbc, 256);
BaseCipherNames.Add(CmsEnvelopedGenerator.DesEde3Cbc, "DESEDE");
BaseCipherNames.Add(CmsEnvelopedGenerator.Aes128Cbc, "AES");
BaseCipherNames.Add(CmsEnvelopedGenerator.Aes192Cbc, "AES");
BaseCipherNames.Add(CmsEnvelopedGenerator.Aes256Cbc, "AES");
// CipherAlgNames.Add(CmsEnvelopedGenerator.DesEde3Cbc, "DESEDE/CBC/PKCS5Padding");
// CipherAlgNames.Add(CmsEnvelopedGenerator.Aes128Cbc, "AES/CBC/PKCS5Padding");
// CipherAlgNames.Add(CmsEnvelopedGenerator.Aes192Cbc, "AES/CBC/PKCS5Padding");
// CipherAlgNames.Add(CmsEnvelopedGenerator.Aes256Cbc, "AES/CBC/PKCS5Padding");
}
private string GetAsymmetricEncryptionAlgName(
string encryptionAlgOid)
{
if (PkcsObjectIdentifiers.RsaEncryption.Id.Equals(encryptionAlgOid))
{
return "RSA/ECB/PKCS1Padding";
}
return encryptionAlgOid;
}
internal IBufferedCipher CreateAsymmetricCipher(
string encryptionOid)
{
try
{
return CipherUtilities.GetCipher(encryptionOid);
}
catch (SecurityUtilityException)
{
return CipherUtilities.GetCipher(GetAsymmetricEncryptionAlgName(encryptionOid));
}
}
internal IWrapper CreateWrapper(
string encryptionOid)
{
try
{
return WrapperUtilities.GetWrapper(encryptionOid);
}
catch (SecurityUtilityException)
{
return WrapperUtilities.GetWrapper(GetAsymmetricEncryptionAlgName(encryptionOid));
}
}
internal string GetRfc3211WrapperName(
string oid)
{
if (oid == null)
throw new ArgumentNullException("oid");
string alg = (string) BaseCipherNames[oid];
if (alg == null)
throw new ArgumentException("no name for " + oid, "oid");
return alg + "RFC3211Wrap";
}
internal int GetKeySize(
string oid)
{
if (!KeySizes.Contains(oid))
{
throw new ArgumentException("no keysize for " + oid, "oid");
}
return (int) KeySizes[oid];
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Org.BouncyCastle.Cms
{
public class CmsException
: Exception
{
public CmsException()
{
}
public CmsException(
string name)
: base(name)
{
}
public CmsException(
string name,
Exception e)
: base(name, e)
{
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
//import javax.crypto.interfaces.PBEKey;
namespace Org.BouncyCastle.Cms
{
public abstract class CmsPbeKey
// TODO Create an equivalent interface somewhere?
// : PBEKey
: ICipherParameters
{
private readonly string password;
private readonly byte[] salt;
private readonly int iterationCount;
public CmsPbeKey(
string password,
byte[] salt,
int iterationCount)
{
this.password = password;
this.salt = Arrays.Clone(salt);
this.iterationCount = iterationCount;
}
public string Password
{
get { return password; }
}
public byte[] Salt
{
get { return Arrays.Clone(salt); }
}
[Obsolete("Use 'Salt' property instead")]
public byte[] GetSalt()
{
return Salt;
}
public int IterationCount
{
get { return iterationCount; }
}
public string Algorithm
{
get { return "PKCS5S2"; }
}
public string Format
{
get { return "RAW"; }
}
public byte[] GetEncoded()
{
return null;
}
internal abstract KeyParameter GetEncoded(string algorithmOid);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.IO;
namespace Org.BouncyCastle.Cms
{
public interface CmsProcessable
{
/// <summary>
/// Return a stream from which the data can be read.
/// </summary>
/// <remarks>
/// This routine may be called more than once, but previous returned
/// streams should be closed first.
/// </remarks>
Stream Read();
/// <summary>
/// Generic routine to copy out the data we want processed.
/// </summary>
/// <remarks>
/// This routine may be called multiple times.
/// </remarks>
void Write(Stream outStream);
object GetContent();
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.IO;
namespace Org.BouncyCastle.Cms
{
/**
* a holding class for a byte array of data to be processed.
*/
public class CmsProcessableByteArray
: CmsProcessable
{
private readonly byte[] bytes;
public CmsProcessableByteArray(
byte[] bytes)
{
this.bytes = bytes;
}
public virtual Stream Read()
{
return new MemoryStream(bytes, false);
}
public virtual void Write(Stream zOut)
{
zOut.Write(bytes, 0, bytes.Length);
}
/// <returns>A clone of the byte array</returns>
public virtual object GetContent()
{
return bytes.Clone();
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.IO;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Cms
{
/**
* a holding class for a file of data to be processed.
*/
public class CmsProcessableFile
: CmsProcessable
{
private const int DefaultBufSize = 32 * 1024;
private readonly FileInfo _file;
private readonly int _bufSize;
public CmsProcessableFile(
FileInfo file)
: this(file, DefaultBufSize)
{
}
public CmsProcessableFile(
FileInfo file,
int bufSize)
{
_file = file;
_bufSize = bufSize;
}
public virtual Stream Read()
{
return new FileStream(
_file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, _bufSize);
}
public virtual void Write(
Stream zOut)
{
Stream inStr = Read();
Streams.PipeAll(inStr, zOut);
inStr.Close();
}
/// <returns>The file handle</returns>
public virtual object GetContent()
{
return _file;
}
}
}

View File

@@ -0,0 +1,430 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
/**
* 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...
*
* <pre>
* IX509Store certs = s.GetCertificates();
* SignerInformationStore signers = s.GetSignerInfos();
*
* foreach (SignerInformation signer in signers.GetSigners())
* {
* ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
* X509Certificate cert = (X509Certificate) certList[0];
*
* if (signer.Verify(cert.GetPublicKey()))
* {
* verified++;
* }
* }
* </pre>
*/
public class CmsSignedData
{
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
private readonly CmsProcessable signedContent;
private SignedData signedData;
private ContentInfo contentInfo;
private SignerInformationStore signerInfoStore;
private IX509Store attrCertStore;
private IX509Store certificateStore;
private IX509Store crlStore;
private IDictionary 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)
: this(CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false)))
{
}
public CmsSignedData(
CmsProcessable signedContent,
byte[] sigBlock)
: this(signedContent, CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false)))
{
}
/**
* 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(
IDictionary hashes,
byte[] sigBlock)
: this(hashes, CmsUtilities.ReadContentInfo(sigBlock))
{
}
/**
* base constructor - content with detached signature.
*
* @param signedContent the content that was signed.
* @param sigData the signature object.
*/
public CmsSignedData(
CmsProcessable signedContent,
Stream sigData)
: this(signedContent, CmsUtilities.ReadContentInfo(sigData))
{
}
/**
* base constructor - with encapsulated content
*/
public CmsSignedData(
Stream sigData)
: this(CmsUtilities.ReadContentInfo(sigData))
{
}
public CmsSignedData(
CmsProcessable signedContent,
ContentInfo sigData)
{
this.signedContent = signedContent;
this.contentInfo = sigData;
this.signedData = SignedData.GetInstance(contentInfo.Content);
}
public CmsSignedData(
IDictionary hashes,
ContentInfo sigData)
{
this.hashes = hashes;
this.contentInfo = sigData;
this.signedData = SignedData.GetInstance(contentInfo.Content);
}
public CmsSignedData(
ContentInfo sigData)
{
this.contentInfo = sigData;
this.signedData = SignedData.GetInstance(contentInfo.Content);
//
// this can happen if the signed message is sent simply to send a
// certificate chain.
//
if (signedData.EncapContentInfo.Content != null)
{
this.signedContent = new CmsProcessableByteArray(
((Asn1OctetString)(signedData.EncapContentInfo.Content)).GetOctets());
}
// else
// {
// this.signedContent = null;
// }
}
/// <summary>Return the version number for this object.</summary>
public int Version
{
get { return signedData.Version.Value.IntValue; }
}
/**
* return the collection of signers that are associated with the
* signatures for the message.
*/
public SignerInformationStore GetSignerInfos()
{
if (signerInfoStore == null)
{
IList signerInfos = new ArrayList();
Asn1Set s = signedData.SignerInfos;
foreach (object obj in s)
{
SignerInfo info = SignerInfo.GetInstance(obj);
DerObjectIdentifier contentType = signedData.EncapContentInfo.ContentType;
if (hashes == null)
{
signerInfos.Add(new SignerInformation(info, contentType, signedContent, null));
}
else
{
byte[] hash = (byte[]) hashes[info.DigestAlgorithm.ObjectID.Id];
signerInfos.Add(new SignerInformation(info, contentType, null, new BaseDigestCalculator(hash)));
}
}
signerInfoStore = new SignerInformationStore(signerInfos);
}
return signerInfoStore;
}
/**
* return a X509Store containing the attribute certificates, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of attribute certificates
* @exception NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetAttributeCertificates(
string type)
{
if (attrCertStore == null)
{
attrCertStore = Helper.CreateAttributeStore(type, signedData.Certificates);
}
return attrCertStore;
}
/**
* return a X509Store containing the public key certificates, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of public key certificates
* @exception NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetCertificates(
string type)
{
if (certificateStore == null)
{
certificateStore = Helper.CreateCertificateStore(type, signedData.Certificates);
}
return certificateStore;
}
/**
* return a X509Store containing CRLs, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of CRLs
* @exception NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetCrls(
string type)
{
if (crlStore == null)
{
crlStore = Helper.CreateCrlStore(type, signedData.CRLs);
}
return crlStore;
}
/**
* Return the a string representation of the OID associated with the
* encapsulated content info structure carried in the signed data.
*
* @return the OID for the content type.
*/
public string SignedContentTypeOid
{
get { return signedData.EncapContentInfo.ContentType.Id; }
}
public CmsProcessable SignedContent
{
get { return signedContent; }
}
/**
* return the ContentInfo
*/
public ContentInfo ContentInfo
{
get { return contentInfo; }
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] GetEncoded()
{
return contentInfo.GetEncoded();
}
/**
* Replace the signerinformation store associated with this
* CmsSignedData object with the new one passed in. You would
* probably only want to do this if you wanted to change the unsigned
* attributes associated with a signer, or perhaps delete one.
*
* @param signedData the signed data object to be used as a base.
* @param signerInformationStore the new signer information store to use.
* @return a new signed data object.
*/
public static CmsSignedData ReplaceSigners(
CmsSignedData signedData,
SignerInformationStore signerInformationStore)
{
//
// copy
//
CmsSignedData cms = new CmsSignedData(signedData);
//
// replace the store
//
cms.signerInfoStore = signerInformationStore;
//
// replace the signers in the SignedData object
//
Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
Asn1EncodableVector vec = new Asn1EncodableVector();
foreach (SignerInformation signer in signerInformationStore.GetSigners())
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
vec.Add(signer.ToSignerInfo());
}
Asn1Set digests = new DerSet(digestAlgs);
Asn1Set signers = new DerSet(vec);
Asn1Sequence sD = (Asn1Sequence)signedData.signedData.ToAsn1Object();
//
// signers are the last item in the sequence.
//
vec = new Asn1EncodableVector(
sD[0], // version
digests);
for (int i = 2; i != sD.Count - 1; i++)
{
vec.Add(sD[i]);
}
vec.Add(signers);
cms.signedData = SignedData.GetInstance(new BerSequence(vec));
//
// replace the contentInfo with the new one
//
cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData);
return cms;
}
/**
* Replace the certificate and CRL information associated with this
* CmsSignedData object with the new one passed in.
*
* @param signedData the signed data object to be used as a base.
* @param x509Certs the new certificates to be used.
* @param x509Crls the new CRLs to be used.
* @return a new signed data object.
* @exception CmsException if there is an error processing the stores
*/
public static CmsSignedData ReplaceCertificatesAndCrls(
CmsSignedData signedData,
IX509Store x509Certs,
IX509Store x509Crls,
IX509Store x509AttrCerts)
{
if (x509AttrCerts != null)
throw Platform.CreateNotImplementedException("Currently can't replace attribute certificates");
//
// copy
//
CmsSignedData cms = new CmsSignedData(signedData);
//
// replace the certs and crls in the SignedData object
//
Asn1Set certs = null;
try
{
Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList(
CmsUtilities.GetCertificatesFromStore(x509Certs));
if (asn1Set.Count != 0)
{
certs = asn1Set;
}
}
catch (X509StoreException e)
{
throw new CmsException("error getting certificates from store", e);
}
Asn1Set crls = null;
try
{
Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList(
CmsUtilities.GetCrlsFromStore(x509Crls));
if (asn1Set.Count != 0)
{
crls = asn1Set;
}
}
catch (X509StoreException e)
{
throw new CmsException("error getting CRLs from store", e);
}
//
// replace the CMS structure.
//
SignedData old = signedData.signedData;
cms.signedData = new SignedData(
old.DigestAlgorithms,
old.EncapContentInfo,
certs,
crls,
old.SignerInfos);
//
// replace the contentInfo with the new one
//
cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData);
return cms;
}
private static AlgorithmIdentifier FixAlgID(
AlgorithmIdentifier algId)
{
if (algId.Parameters == null)
return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
return algId;
}
}
}

View File

@@ -0,0 +1,501 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* general class for generating a pkcs7-signature message.
* <p>
* A simple example of usage.
*
* <pre>
* IX509Store certs...
* IX509Store crls...
* CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
*
* gen.AddSigner(privKey, cert, CmsSignedGenerator.DigestSha1);
* gen.AddCertificates(certs);
* gen.AddCrls(crls);
*
* CmsSignedData data = gen.Generate(content);
* </pre>
* </p>
*/
public class CmsSignedDataGenerator
: CmsSignedGenerator
{
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
private readonly ArrayList signerInfs = new ArrayList();
internal class DigOutputStream
: BaseOutputStream
{
private readonly IDigest dig;
public DigOutputStream(
IDigest dig)
{
this.dig = dig;
}
public override void Write(
byte[] b,
int off,
int len)
{
dig.BlockUpdate(b, off, len);
}
public override void WriteByte(
byte b)
{
dig.Update(b);
}
}
internal class SigOutputStream
: BaseOutputStream
{
private readonly ISigner sig;
public SigOutputStream(
ISigner sig)
{
this.sig = sig;
}
public override void Write(
byte[] b,
int off,
int len)
{
try
{
sig.BlockUpdate(b, off, len);
}
catch (SignatureException e)
{
throw new IOException("signature problem: " + e);
}
}
public override void WriteByte(
byte b)
{
try
{
sig.Update(b);
}
catch (SignatureException e)
{
throw new IOException("signature problem: " + e);
}
}
}
private class SignerInf
{
CmsSignedGenerator outer;
AsymmetricKeyParameter key;
X509Certificate cert;
string digestOID;
string encOID;
CmsAttributeTableGenerator sAttr;
CmsAttributeTableGenerator unsAttr;
Asn1.Cms.AttributeTable baseSignedTable;
internal SignerInf(
CmsSignedGenerator outer,
AsymmetricKeyParameter key,
X509Certificate cert,
string digestOID,
string encOID)
{
this.outer = outer;
this.key = key;
this.cert = cert;
this.digestOID = digestOID;
this.encOID = encOID;
}
internal SignerInf(
CmsSignedGenerator outer,
AsymmetricKeyParameter key,
X509Certificate cert,
string digestOID,
string encOID,
CmsAttributeTableGenerator sAttr,
CmsAttributeTableGenerator unsAttr,
Asn1.Cms.AttributeTable baseSignedTable)
{
this.outer = outer;
this.key = key;
this.cert = cert;
this.digestOID = digestOID;
this.encOID = encOID;
this.sAttr = sAttr;
this.unsAttr = unsAttr;
this.baseSignedTable = baseSignedTable;
}
internal AsymmetricKeyParameter GetKey()
{
return key;
}
internal X509Certificate GetCertificate()
{
return cert;
}
internal AlgorithmIdentifier DigestAlgorithmID
{
get { return new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), null); }
}
internal string DigestAlgOid
{
get { return digestOID; }
}
internal Asn1Object DigestAlgParams
{
get { return null; }
}
internal AlgorithmIdentifier EncryptionAlgorithmID
{
get { return new AlgorithmIdentifier(new DerObjectIdentifier(encOID), DerNull.Instance); }
}
internal string EncryptionAlgOid
{
get { return encOID; }
}
internal CmsAttributeTableGenerator SignedAttributes
{
get { return sAttr; }
}
internal CmsAttributeTableGenerator UnsignedAttributes
{
get { return unsAttr; }
}
internal Asn1.Cms.SignerInfo ToSignerInfo(
DerObjectIdentifier contentType,
CmsProcessable content,
SecureRandom random,
bool isCounterSignature)
{
AlgorithmIdentifier digAlgId = new AlgorithmIdentifier(
new DerObjectIdentifier(this.DigestAlgOid), DerNull.Instance);
AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier(this.EncryptionAlgOid);
string digestName = Helper.GetDigestAlgName(digestOID);
string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID);
ISigner sig = Helper.GetSignatureInstance(signatureName);
IDigest dig = Helper.GetDigestInstance(digestName);
byte[] hash = null;
if (content != null)
{
content.Write(new DigOutputStream(dig));
hash = DigestUtilities.DoFinal(dig);
outer._digests.Add(digestOID, hash.Clone());
}
IDictionary parameters = outer.GetBaseParameters(contentType, digAlgId, hash);
Asn1.Cms.AttributeTable signed = (sAttr != null)
// ? sAttr.GetAttributes(Collections.unmodifiableMap(parameters))
? sAttr.GetAttributes(parameters)
: null;
if (isCounterSignature)
{
Hashtable ats = signed.ToHashtable();
ats.Remove(CmsAttributes.ContentType);
signed = new Asn1.Cms.AttributeTable(ats);
}
Asn1Set signedAttr = outer.GetAttributeSet(signed);
//
// sig must be composed from the DER encoding.
//
byte[] tmp;
if (signedAttr != null)
{
tmp = signedAttr.GetEncoded(Asn1Encodable.Der);
}
else
{
MemoryStream bOut = new MemoryStream();
content.Write(bOut);
tmp = bOut.ToArray();
}
sig.Init(true, new ParametersWithRandom(key, random));
sig.BlockUpdate(tmp, 0, tmp.Length);
Asn1OctetString encDigest = new DerOctetString(sig.GenerateSignature());
IDictionary baseParameters = outer.GetBaseParameters(contentType, digAlgId, hash);
baseParameters[CmsAttributeTableParameter.Signature] = encDigest.GetOctets().Clone();
Asn1.Cms.AttributeTable unsigned = (unsAttr != null)
// ? unsAttr.GetAttributes(Collections.unmodifiableMap(baseParameters))
? unsAttr.GetAttributes(baseParameters)
: null;
Asn1Set unsignedAttr = outer.GetAttributeSet(unsigned);
X509Certificate cert = this.GetCertificate();
TbsCertificateStructure tbs = TbsCertificateStructure.GetInstance(
Asn1Object.FromByteArray(cert.GetTbsCertificate()));
Asn1.Cms.IssuerAndSerialNumber encSid = new Asn1.Cms.IssuerAndSerialNumber(
tbs.Issuer, tbs.SerialNumber.Value);
return new Asn1.Cms.SignerInfo(new SignerIdentifier(encSid), digAlgId,
signedAttr, encAlgId, encDigest, unsignedAttr);
}
}
public CmsSignedDataGenerator()
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
public CmsSignedDataGenerator(
SecureRandom rand)
: base(rand)
{
}
/**
* add a signer - no attributes other than the default ones will be
* provided here.
*/
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID)
{
string encOID = GetEncOid(privateKey, digestOID);
signerInfs.Add(new SignerInf(this, privateKey, cert, digestOID, encOID,
new DefaultSignedAttributeTableGenerator(), null, null));
}
/**
* add a signer with extra signed/unsigned attributes.
*/
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID,
Asn1.Cms.AttributeTable signedAttr,
Asn1.Cms.AttributeTable unsignedAttr)
{
string encOID = GetEncOid(privateKey, digestOID);
signerInfs.Add(new SignerInf(this, privateKey, cert, digestOID, encOID,
new DefaultSignedAttributeTableGenerator(signedAttr),
new SimpleAttributeTableGenerator(unsignedAttr),
signedAttr));
}
/**
* add a signer with extra signed/unsigned attributes based on generators.
*/
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID,
CmsAttributeTableGenerator signedAttrGen,
CmsAttributeTableGenerator unsignedAttrGen)
{
string encOID = GetEncOid(privateKey, digestOID);
signerInfs.Add(new SignerInf(this, privateKey, cert, digestOID, encOID,
signedAttrGen, unsignedAttrGen, null));
}
private static AlgorithmIdentifier FixAlgID(
AlgorithmIdentifier algId)
{
if (algId.Parameters == null)
return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
return algId;
}
/**
* generate a signed object that for a CMS Signed Data object
*/
public CmsSignedData Generate(
CmsProcessable content)
{
return Generate(content, false);
}
/**
* generate a signed object that for a CMS Signed Data
* object - 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 CmsSignedData Generate(
string signedContentType,
CmsProcessable content,
bool encapsulate)
{
Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
Asn1EncodableVector signerInfos = new Asn1EncodableVector();
_digests.Clear(); // clear the current preserved digest state
//
// add the precalculated SignerInfo objects.
//
foreach (SignerInformation signer in _signers)
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
signerInfos.Add(signer.ToSignerInfo());
}
//
// add the SignerInfo objects
//
DerObjectIdentifier contentTypeOID;
bool isCounterSignature;
if (signedContentType != null)
{
contentTypeOID = new DerObjectIdentifier(signedContentType);
isCounterSignature = false;
}
else
{
contentTypeOID = CmsObjectIdentifiers.Data;
isCounterSignature = true;
}
foreach (SignerInf signer in signerInfs)
{
try
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
signerInfos.Add(signer.ToSignerInfo(contentTypeOID, content, rand, isCounterSignature));
}
catch (IOException e)
{
throw new CmsException("encoding error.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key inappropriate for signature.", e);
}
catch (SignatureException e)
{
throw new CmsException("error creating signature.", e);
}
catch (CertificateEncodingException e)
{
throw new CmsException("error creating sid.", e);
}
}
Asn1Set certificates = null;
if (_certs.Count != 0)
{
certificates = CmsUtilities.CreateBerSetFromList(_certs);
}
Asn1Set certrevlist = null;
if (_crls.Count != 0)
{
certrevlist = CmsUtilities.CreateBerSetFromList(_crls);
}
Asn1OctetString octs = null;
if (encapsulate)
{
MemoryStream bOut = new MemoryStream();
try
{
content.Write(bOut);
}
catch (IOException e)
{
throw new CmsException("encapsulation error.", e);
}
octs = new BerOctetString(bOut.ToArray());
}
Asn1.Cms.ContentInfo encInfo = new Asn1.Cms.ContentInfo(contentTypeOID, octs);
Asn1.Cms.SignedData sd = new Asn1.Cms.SignedData(
new DerSet(digestAlgs),
encInfo,
certificates,
certrevlist,
new DerSet(signerInfos));
Asn1.Cms.ContentInfo contentInfo = new Asn1.Cms.ContentInfo(
PkcsObjectIdentifiers.SignedData, sd);
return new CmsSignedData(content, contentInfo);
}
/**
* generate a signed object that for a CMS Signed Data
* object - if encapsulate is true a copy
* of the message will be included in the signature with the
* default content type "data".
*/
public CmsSignedData Generate(
CmsProcessable content,
bool encapsulate)
{
return this.Generate(Data, content, encapsulate);
}
/**
* generate a set of one or more SignerInformation objects representing counter signatures on
* the passed in SignerInformation object.
*
* @param signer the signer to be countersigned
* @param sigProvider the provider to be used for counter signing.
* @return a store containing the signers.
*/
public SignerInformationStore GenerateCounterSigners(
SignerInformation signer)
{
return this.Generate(null, new CmsProcessableByteArray(signer.GetSignature()), false).GetSignerInfos();
}
}
}

View File

@@ -0,0 +1,565 @@
using System;
using System.Collections;
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.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
/**
* Parsing class for an CMS Signed Data object from an input stream.
* <p>
* Note: that because we are in a streaming mode only one signer can be tried and it is important
* that the methods on the parser are called in the appropriate order.
* </p>
* <p>
* A simple example of usage for an encapsulated signature.
* </p>
* <p>
* Two notes: first, in the example below the validity of
* the certificate isn't verified, just the fact that one of the certs
* matches the given signer, and, second, because we are in a streaming
* mode the order of the operations is important.
* </p>
* <pre>
* CmsSignedDataParser sp = new CmsSignedDataParser(encapSigData);
*
* sp.GetSignedContent().Drain();
*
* IX509Store certs = sp.GetCertificates();
* SignerInformationStore signers = sp.GetSignerInfos();
*
* foreach (SignerInformation signer in signers.GetSigners())
* {
* ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
* X509Certificate cert = (X509Certificate) certList[0];
*
* Console.WriteLine("verify returns: " + signer.Verify(cert));
* }
* </pre>
* Note also: this class does not introduce buffering - if you are processing large files you should create
* the parser with:
* <pre>
* CmsSignedDataParser ep = new CmsSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
* </pre>
* where bufSize is a suitably large buffer size.
*/
public class CmsSignedDataParser
: CmsContentInfoParser
{
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
private SignedDataParser _signedData;
private CmsTypedStream _signedContent;
private IDictionary _digests;
private SignerInformationStore _signerInfoStore;
private Asn1Set _certSet, _crlSet;
private bool _isCertCrlParsed;
private IX509Store _attributeStore;
private IX509Store _certificateStore;
private IX509Store _crlStore;
public CmsSignedDataParser(
byte[] sigBlock)
: this(new MemoryStream(sigBlock, false))
{
}
public CmsSignedDataParser(
CmsTypedStream signedContent,
byte[] sigBlock)
: this(signedContent, new MemoryStream(sigBlock, false))
{
}
/**
* base constructor - with encapsulated content
*/
public CmsSignedDataParser(
Stream sigData)
: this(null, sigData)
{
}
/**
* base constructor
*
* @param signedContent the content that was signed.
* @param sigData the signature object.
*/
public CmsSignedDataParser(
CmsTypedStream signedContent,
Stream sigData)
: base(sigData)
{
try
{
this._signedContent = signedContent;
this._signedData = SignedDataParser.GetInstance(this.contentInfo.GetContent(Asn1Tags.Sequence));
this._digests = new Hashtable();
Asn1SetParser digAlgs = _signedData.GetDigestAlgorithms();
IAsn1Convertible o;
while ((o = digAlgs.ReadObject()) != null)
{
AlgorithmIdentifier id = AlgorithmIdentifier.GetInstance(o.ToAsn1Object());
try
{
string digestName = Helper.GetDigestAlgName(id.ObjectID.Id);
IDigest dig = Helper.GetDigestInstance(digestName);
this._digests[digestName] = dig;
}
catch (SecurityUtilityException)
{
// ignore
}
}
//
// If the message is simply a certificate chain message GetContent() may return null.
//
ContentInfoParser cont = _signedData.GetEncapContentInfo();
Asn1OctetStringParser octs = (Asn1OctetStringParser)
cont.GetContent(Asn1Tags.OctetString);
if (octs != null)
{
CmsTypedStream ctStr = new CmsTypedStream(
cont.ContentType.Id, octs.GetOctetStream());
if (_signedContent == null)
{
this._signedContent = ctStr;
}
else
{
//
// content passed in, need to read past empty encapsulated content info object if present
//
ctStr.Drain();
}
}
}
catch (IOException e)
{
throw new CmsException("io exception: " + e.Message, e);
}
if (_digests.Count < 1)
{
throw new CmsException("no digests could be created for message.");
}
}
/**
* Return the version number for the SignedData object
*
* @return the version number
*/
public int Version
{
get { return _signedData.Version.Value.IntValue; }
}
/**
* return the collection of signers that are associated with the
* signatures for the message.
* @throws CmsException
*/
public SignerInformationStore GetSignerInfos()
{
if (_signerInfoStore == null)
{
PopulateCertCrlSets();
IList signerInfos = new ArrayList();
IDictionary hashes = new Hashtable();
foreach (object digestKey in _digests.Keys)
{
hashes[digestKey] = DigestUtilities.DoFinal(
(IDigest)_digests[digestKey]);
}
try
{
Asn1SetParser s = _signedData.GetSignerInfos();
IAsn1Convertible o;
while ((o = s.ReadObject()) != null)
{
SignerInfo info = SignerInfo.GetInstance(o.ToAsn1Object());
string digestName = Helper.GetDigestAlgName(
info.DigestAlgorithm.ObjectID.Id);
byte[] hash = (byte[]) hashes[digestName];
DerObjectIdentifier oid = new DerObjectIdentifier(_signedContent.ContentType);
signerInfos.Add(new SignerInformation(info, oid, null, new BaseDigestCalculator(hash)));
}
}
catch (IOException e)
{
throw new CmsException("io exception: " + e.Message, e);
}
_signerInfoStore = new SignerInformationStore(signerInfos);
}
return _signerInfoStore;
}
/**
* return a X509Store containing the attribute certificates, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of attribute certificates
* @exception org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetAttributeCertificates(
string type)
{
if (_attributeStore == null)
{
PopulateCertCrlSets();
_attributeStore = Helper.CreateAttributeStore(type, _certSet);
}
return _attributeStore;
}
/**
* return a X509Store containing the public key certificates, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of public key certificates
* @exception NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetCertificates(
string type)
{
if (_certificateStore == null)
{
PopulateCertCrlSets();
_certificateStore = Helper.CreateCertificateStore(type, _certSet);
}
return _certificateStore;
}
/**
* return a X509Store containing CRLs, if any, contained
* in this message.
*
* @param type type of store to create
* @return a store of CRLs
* @exception NoSuchStoreException if the store type isn't available.
* @exception CmsException if a general exception prevents creation of the X509Store
*/
public IX509Store GetCrls(
string type)
{
if (_crlStore == null)
{
PopulateCertCrlSets();
_crlStore = Helper.CreateCrlStore(type, _crlSet);
}
return _crlStore;
}
private void PopulateCertCrlSets()
{
if (_isCertCrlParsed)
return;
_isCertCrlParsed = true;
try
{
// care! Streaming - Must process the GetCertificates() result before calling GetCrls()
_certSet = GetAsn1Set(_signedData.GetCertificates());
_crlSet = GetAsn1Set(_signedData.GetCrls());
}
catch (IOException e)
{
throw new CmsException("problem parsing cert/crl sets", e);
}
}
public CmsTypedStream GetSignedContent()
{
if (_signedContent == null)
{
return null;
}
Stream digStream = _signedContent.ContentStream;
foreach (IDigest digest in _digests.Values)
{
digStream = new DigestStream(digStream, digest, null);
}
return new CmsTypedStream(_signedContent.ContentType, digStream);
}
/**
* Replace the signerinformation store associated with the passed
* in message contained in the stream original with the new one passed in.
* You would probably only want to do this if you wanted to change the unsigned
* attributes associated with a signer, or perhaps delete one.
* <p>
* The output stream is returned unclosed.
* </p>
* @param original the signed data stream to be used as a base.
* @param signerInformationStore the new signer information store to use.
* @param out the stream to Write the new signed data object to.
* @return out.
*/
public static Stream ReplaceSigners(
Stream original,
SignerInformationStore signerInformationStore,
Stream outStr)
{
Asn1StreamParser inStr = new Asn1StreamParser(original, CmsUtilities.MaximumMemory);
ContentInfoParser contentInfo = new ContentInfoParser((Asn1SequenceParser)inStr.ReadObject());
SignedDataParser signedData = SignedDataParser.GetInstance(contentInfo.GetContent(Asn1Tags.Sequence));
BerSequenceGenerator sGen = new BerSequenceGenerator(outStr);
sGen.AddObject(CmsObjectIdentifiers.SignedData);
BerSequenceGenerator sigGen = new BerSequenceGenerator(sGen.GetRawOutputStream(), 0, true);
// version number
sigGen.AddObject(signedData.Version);
// digests
signedData.GetDigestAlgorithms().ToAsn1Object(); // skip old ones
Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
foreach (SignerInformation signer in signerInformationStore.GetSigners())
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
}
WriteToGenerator(sigGen, new DerSet(digestAlgs));
// encap content info
ContentInfoParser encapContentInfo = signedData.GetEncapContentInfo();
BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream());
eiGen.AddObject(encapContentInfo.ContentType);
Asn1OctetStringParser octs = (Asn1OctetStringParser)encapContentInfo.GetContent(Asn1Tags.OctetString);
if (octs != null)
{
PipeOctetString(octs, eiGen.GetRawOutputStream());
}
eiGen.Close();
WriteSetToGeneratorTagged(sigGen, signedData.GetCertificates(), 0);
WriteSetToGeneratorTagged(sigGen, signedData.GetCrls(), 1);
Asn1EncodableVector signerInfos = new Asn1EncodableVector();
foreach (SignerInformation signer in signerInformationStore.GetSigners())
{
signerInfos.Add(signer.ToSignerInfo());
}
WriteToGenerator(sigGen, new DerSet(signerInfos));
sigGen.Close();
sGen.Close();
return outStr;
}
/**
* Replace the certificate and CRL information associated with this
* CMSSignedData object with the new one passed in.
* <p>
* The output stream is returned unclosed.
* </p>
* @param original the signed data stream to be used as a base.
* @param certsAndCrls the new certificates and CRLs to be used.
* @param out the stream to Write the new signed data object to.
* @return out.
* @exception CmsException if there is an error processing the CertStore
*/
public static Stream ReplaceCertificatesAndCrls(
Stream original,
IX509Store x509Certs,
IX509Store x509Crls,
IX509Store x509AttrCerts,
Stream outStr)
{
if (x509AttrCerts != null)
throw Platform.CreateNotImplementedException("Currently can't replace attribute certificates");
Asn1StreamParser inStr = new Asn1StreamParser(original, CmsUtilities.MaximumMemory);
ContentInfoParser contentInfo = new ContentInfoParser((Asn1SequenceParser)inStr.ReadObject());
SignedDataParser signedData = SignedDataParser.GetInstance(contentInfo.GetContent(Asn1Tags.Sequence));
BerSequenceGenerator sGen = new BerSequenceGenerator(outStr);
sGen.AddObject(CmsObjectIdentifiers.SignedData);
BerSequenceGenerator sigGen = new BerSequenceGenerator(sGen.GetRawOutputStream(), 0, true);
// version number
sigGen.AddObject(signedData.Version);
// digests
WriteToGenerator(sigGen, signedData.GetDigestAlgorithms().ToAsn1Object());
// encap content info
ContentInfoParser encapContentInfo = signedData.GetEncapContentInfo();
BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream());
eiGen.AddObject(encapContentInfo.ContentType);
Asn1OctetStringParser octs = (Asn1OctetStringParser)
encapContentInfo.GetContent(Asn1Tags.OctetString);
if (octs != null)
{
PipeOctetString(octs, eiGen.GetRawOutputStream());
}
eiGen.Close();
//
// skip existing certs and CRLs
//
GetAsn1Set(signedData.GetCertificates());
GetAsn1Set(signedData.GetCrls());
//
// replace the certs and crls in the SignedData object
//
Asn1Set certs;
try
{
certs = CmsUtilities.CreateBerSetFromList(
CmsUtilities.GetCertificatesFromStore(x509Certs));
}
catch (X509StoreException e)
{
throw new CmsException("error getting certs from certStore", e);
}
if (certs.Count > 0)
{
WriteToGenerator(sigGen, new DerTaggedObject(false, 0, certs));
}
Asn1Set crls;
try
{
crls = CmsUtilities.CreateBerSetFromList(
CmsUtilities.GetCrlsFromStore(x509Crls));
}
catch (X509StoreException e)
{
throw new CmsException("error getting crls from certStore", e);
}
if (crls.Count > 0)
{
WriteToGenerator(sigGen, new DerTaggedObject(false, 1, crls));
}
WriteToGenerator(sigGen, signedData.GetSignerInfos().ToAsn1Object());
sigGen.Close();
sGen.Close();
return outStr;
}
private static AlgorithmIdentifier FixAlgID(
AlgorithmIdentifier algId)
{
if (algId.Parameters == null)
return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
return algId;
}
private static void WriteSetToGeneratorTagged(
Asn1Generator asn1Gen,
Asn1SetParser asn1SetParser,
int tagNo)
{
Asn1Set asn1Set = GetAsn1Set(asn1SetParser);
if (asn1Set != null)
{
Asn1TaggedObject taggedObj = (asn1SetParser is BerSetParser)
? new BerTaggedObject(false, tagNo, asn1Set)
: new DerTaggedObject(false, tagNo, asn1Set);
WriteToGenerator(asn1Gen, taggedObj);
}
}
private static Asn1Set GetAsn1Set(
Asn1SetParser asn1SetParser)
{
return asn1SetParser == null
? null
: Asn1Set.GetInstance(asn1SetParser.ToAsn1Object());
}
private static void WriteToGenerator(
Asn1Generator ag,
Asn1Encodable ae)
{
byte[] encoded = ae.GetEncoded();
ag.GetRawOutputStream().Write(encoded, 0, encoded.Length);
}
private static void PipeOctetString(
Asn1OctetStringParser octs,
Stream output)
{
BerOctetStringGenerator octGen = new BerOctetStringGenerator(output, 0, true);
// TODO Allow specification of a specific fragment size?
Stream outOctets = octGen.GetOctetOutputStream();
Streams.PipeAll(octs.GetOctetStream(), outOctets);
outOctets.Close();
}
}
}

View File

@@ -0,0 +1,690 @@
using System;
using System.Collections;
using System.Diagnostics;
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.Security.Certificates;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* General class for generating a pkcs7-signature message stream.
* <p>
* A simple example of usage.
* </p>
* <pre>
* IX509Store certs...
* CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
*
* gen.AddSigner(privateKey, cert, CmsSignedDataStreamGenerator.DIGEST_SHA1);
*
* gen.AddCertificates(certs);
*
* Stream sigOut = gen.Open(bOut);
*
* sigOut.Write(Encoding.UTF8.GetBytes("Hello World!"));
*
* sigOut.Close();
* </pre>
*/
public class CmsSignedDataStreamGenerator
: CmsSignedGenerator
{
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
private readonly ArrayList _signerInfs = new ArrayList();
private readonly ArrayList _messageDigests = new ArrayList();
private int _bufferSize;
private class SignerInf
{
private readonly CmsSignedDataStreamGenerator outer;
AsymmetricKeyParameter _key;
X509Certificate _cert;
string _digestOID;
string _encOID;
CmsAttributeTableGenerator _sAttr;
CmsAttributeTableGenerator _unsAttr;
IDigest _digest;
ISigner _signature;
internal SignerInf(
CmsSignedDataStreamGenerator outer,
AsymmetricKeyParameter key,
X509Certificate cert,
string digestOID,
string encOID,
CmsAttributeTableGenerator sAttr,
CmsAttributeTableGenerator unsAttr,
IDigest digest,
ISigner signature)
{
this.outer = outer;
_key = key;
_cert = cert;
_digestOID = digestOID;
_encOID = encOID;
_sAttr = sAttr;
_unsAttr = unsAttr;
_digest = digest;
_signature = signature;
}
internal AsymmetricKeyParameter Key
{
get { return _key; }
}
internal X509Certificate Certificate
{
get { return _cert; }
}
internal AlgorithmIdentifier DigestAlgorithmID
{
get { return new AlgorithmIdentifier(new DerObjectIdentifier(_digestOID), null); }
}
internal string DigestAlgOid
{
get { return _digestOID; }
}
internal Asn1Object DigestAlgParams
{
get { return null; }
}
internal string EncryptionAlgOid
{
get { return _encOID; }
}
// internal Asn1.Cms.AttributeTable SignedAttributes
// {
// get { return _sAttr; }
// }
//
// internal Asn1.Cms.AttributeTable UnsignedAttributes
// {
// get { return _unsAttr; }
// }
internal SignerInfo ToSignerInfo(
DerObjectIdentifier contentType)
{
AlgorithmIdentifier digAlgId = new AlgorithmIdentifier(
new DerObjectIdentifier(this.DigestAlgOid), DerNull.Instance);
AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier(this.EncryptionAlgOid);
byte[] hash = DigestUtilities.DoFinal(_digest);
outer._digests.Add(_digestOID, hash.Clone());
IDictionary parameters = outer.GetBaseParameters(contentType, digAlgId, hash);
Asn1.Cms.AttributeTable signed = (_sAttr != null)
// ? _sAttr.GetAttributes(Collections.unmodifiableMap(parameters))
? _sAttr.GetAttributes(parameters)
: null;
Asn1Set signedAttr = outer.GetAttributeSet(signed);
//
// sig must be composed from the DER encoding.
//
byte[] tmp;
if (signedAttr != null)
{
tmp = signedAttr.GetEncoded(Asn1Encodable.Der);
}
else
{
throw new Exception("signatures without signed attributes not implemented.");
}
_signature.BlockUpdate(tmp, 0, tmp.Length);
Asn1OctetString encDigest = new DerOctetString(_signature.GenerateSignature());
parameters = outer.GetBaseParameters(contentType, digAlgId, hash);
parameters[CmsAttributeTableParameter.Signature] = encDigest.GetOctets().Clone();
Asn1.Cms.AttributeTable unsigned = (_unsAttr != null)
// ? _unsAttr.getAttributes(Collections.unmodifiableMap(parameters))
? _unsAttr.GetAttributes(parameters)
: null;
Asn1Set unsignedAttr = outer.GetAttributeSet(unsigned);
X509Certificate cert = this.Certificate;
TbsCertificateStructure tbs = TbsCertificateStructure.GetInstance(
Asn1Object.FromByteArray(cert.GetTbsCertificate()));
IssuerAndSerialNumber encSid = new IssuerAndSerialNumber(
tbs.Issuer, tbs.SerialNumber.Value);
return new SignerInfo(new SignerIdentifier(encSid), digAlgId,
signedAttr, encAlgId, encDigest, unsignedAttr);
}
}
public CmsSignedDataStreamGenerator()
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
public CmsSignedDataStreamGenerator(
SecureRandom rand)
: base(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 NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID)
{
AddSigner(privateKey, cert, digestOID,
new DefaultSignedAttributeTableGenerator(), null);
}
/**
* add a signer with extra signed/unsigned attributes.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID,
Asn1.Cms.AttributeTable signedAttr,
Asn1.Cms.AttributeTable unsignedAttr)
{
AddSigner(privateKey, cert, digestOID,
new DefaultSignedAttributeTableGenerator(signedAttr),
new SimpleAttributeTableGenerator(unsignedAttr));
}
public void AddSigner(
AsymmetricKeyParameter privateKey,
X509Certificate cert,
string digestOID,
CmsAttributeTableGenerator signedAttrGenerator,
CmsAttributeTableGenerator unsignedAttrGenerator)
{
string encOID = GetEncOid(privateKey, digestOID);
string digestName = Helper.GetDigestAlgName(digestOID);
string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID);
ISigner sig = Helper.GetSignatureInstance(signatureName);
IDigest dig = Helper.GetDigestInstance(digestName);
sig.Init(true, new ParametersWithRandom(privateKey, rand));
_signerInfs.Add(new SignerInf(this, privateKey, cert, digestOID, encOID,
signedAttrGenerator, unsignedAttrGenerator, dig, sig));
_messageDigests.Add(dig);
}
private static AlgorithmIdentifier FixAlgID(
AlgorithmIdentifier algId)
{
if (algId.Parameters == null)
return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
return algId;
}
/**
* generate a signed object that for a CMS Signed Data object
*/
public Stream Open(
Stream outStream)
{
return Open(outStream, false);
}
/**
* generate a signed object that for a CMS Signed Data
* object - if encapsulate is true a copy
* of the message will be included in the signature with the
* default content type "data".
*/
public Stream Open(
Stream outStream,
bool encapsulate)
{
return Open(outStream, Data, 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 Stream Open(
Stream outStream,
bool encapsulate,
Stream dataOutputStream)
{
return Open(outStream, Data, encapsulate, dataOutputStream);
}
/**
* generate a signed object that for a CMS Signed Data
* object - 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 Stream Open(
Stream outStream,
string signedContentType,
bool encapsulate)
{
return Open(outStream, signedContentType, 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.
* @param out stream the CMS object is to be written to.
* @param signedContentType OID for data to be signed.
* @param encapsulate true if data should be encapsulated.
* @param dataOutputStream output stream to copy the data being signed to.
*/
public Stream Open(
Stream outStream,
string signedContentType,
bool encapsulate,
Stream dataOutputStream)
{
if (outStream == null)
throw new ArgumentNullException("outStream");
if (!outStream.CanWrite)
throw new ArgumentException("Expected writeable stream", "outStream");
if (dataOutputStream != null && !dataOutputStream.CanWrite)
throw new ArgumentException("Expected writeable stream", "dataOutputStream");
//
// ContentInfo
//
BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
sGen.AddObject(CmsObjectIdentifiers.SignedData);
//
// Signed Data
//
BerSequenceGenerator sigGen = new BerSequenceGenerator(
sGen.GetRawOutputStream(), 0, true);
sigGen.AddObject(CalculateVersion(signedContentType));
Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
//
// add the precalculated SignerInfo digest algorithms.
//
foreach (SignerInformation signer in _signers)
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
}
//
// add the new digests
//
foreach (SignerInf signer in _signerInfs)
{
digestAlgs.Add(FixAlgID(signer.DigestAlgorithmID));
}
{
byte[] tmp = new DerSet(digestAlgs).GetEncoded();
sigGen.GetRawOutputStream().Write(tmp, 0, tmp.Length);
}
BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream());
eiGen.AddObject(new DerObjectIdentifier(signedContentType));
Stream digStream;
if (encapsulate)
{
BerOctetStringGenerator octGen = new BerOctetStringGenerator(
eiGen.GetRawOutputStream(), 0, true);
digStream = octGen.GetOctetOutputStream(_bufferSize);
if (dataOutputStream != null)
{
digStream = new TeeOutputStream(dataOutputStream, digStream);
}
}
else
{
if (dataOutputStream != null)
{
digStream = dataOutputStream;
}
else
{
digStream = new NullOutputStream();
}
}
foreach (IDigest d in _messageDigests)
{
digStream = new DigestStream(digStream, null, d);
}
return new CmsSignedDataOutputStream(this, digStream, signedContentType, sGen, sigGen, eiGen);
}
// 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(
string contentOid)
{
bool otherCert = false;
bool otherCrl = false;
bool attrCertV1Found = false;
bool attrCertV2Found = false;
if (_certs != null)
{
foreach (object obj in _certs)
{
if (obj is Asn1TaggedObject)
{
Asn1TaggedObject tagged = (Asn1TaggedObject) obj;
if (tagged.TagNo == 1)
{
attrCertV1Found = true;
}
else if (tagged.TagNo == 2)
{
attrCertV2Found = true;
}
else if (tagged.TagNo == 3)
{
otherCert = true;
break;
}
}
}
}
if (otherCert)
{
return new DerInteger(5);
}
if (_crls != null)
{
foreach (object obj in _crls)
{
if (obj is Asn1TaggedObject)
{
otherCrl = true;
break;
}
}
}
if (otherCrl)
{
return new DerInteger(5);
}
if (attrCertV2Found)
{
return new DerInteger(4);
}
if (attrCertV1Found)
{
return new DerInteger(3);
}
if (contentOid.Equals(Data)
&& !CheckForVersion3(_signers))
{
return new DerInteger(1);
}
return new DerInteger(3);
}
private bool CheckForVersion3(
IList signerInfos)
{
foreach (SignerInformation si in signerInfos)
{
SignerInfo s = SignerInfo.GetInstance(si.ToSignerInfo());
if (s.Version.Value.IntValue == 3)
{
return true;
}
}
return false;
}
private class NullOutputStream
: BaseOutputStream
{
public override void WriteByte(
byte b)
{
// do nothing
}
public override void Write(
byte[] buffer,
int offset,
int count)
{
// do nothing
}
}
private class TeeOutputStream
: BaseOutputStream
{
private readonly Stream s1, s2;
public TeeOutputStream(Stream dataOutputStream, Stream digStream)
{
Debug.Assert(dataOutputStream.CanWrite);
Debug.Assert(digStream.CanWrite);
this.s1 = dataOutputStream;
this.s2 = digStream;
}
public override void Write(byte[] buffer, int offset, int count)
{
s1.Write(buffer, offset, count);
s2.Write(buffer, offset, count);
}
public override void WriteByte(byte b)
{
s1.WriteByte(b);
s2.WriteByte(b);
}
public override void Close()
{
s1.Close();
s2.Close();
}
}
private class CmsSignedDataOutputStream
: BaseOutputStream
{
private readonly CmsSignedDataStreamGenerator outer;
private Stream _out;
private DerObjectIdentifier _contentOID;
private BerSequenceGenerator _sGen;
private BerSequenceGenerator _sigGen;
private BerSequenceGenerator _eiGen;
public CmsSignedDataOutputStream(
CmsSignedDataStreamGenerator outer,
Stream outStream,
string contentOID,
BerSequenceGenerator sGen,
BerSequenceGenerator sigGen,
BerSequenceGenerator eiGen)
{
this.outer = outer;
_out = outStream;
_contentOID = new DerObjectIdentifier(contentOID);
_sGen = sGen;
_sigGen = sigGen;
_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();
_eiGen.Close();
outer._digests.Clear(); // clear the current preserved digest state
if (outer._certs.Count > 0)
{
Asn1Set certs = CmsUtilities.CreateBerSetFromList(outer._certs);
WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs));
}
if (outer._crls.Count > 0)
{
Asn1Set crls = CmsUtilities.CreateBerSetFromList(outer._crls);
WriteToGenerator(_sigGen, new BerTaggedObject(false, 1, crls));
}
//
// add the precalculated SignerInfo objects.
//
Asn1EncodableVector signerInfos = new Asn1EncodableVector();
foreach (SignerInformation signer in outer._signers)
{
signerInfos.Add(signer.ToSignerInfo());
}
//
// add the SignerInfo objects
//
foreach (SignerInf signer in outer._signerInfs)
{
try
{
signerInfos.Add(signer.ToSignerInfo(_contentOID));
}
catch (IOException e)
{
throw new IOException("encoding error." + e);
}
catch (SignatureException e)
{
throw new IOException("error creating signature." + e);
}
catch (CertificateEncodingException e)
{
throw new IOException("error creating sid." + e);
}
}
WriteToGenerator(_sigGen, new DerSet(signerInfos));
_sigGen.Close();
_sGen.Close();
base.Close();
}
private static void WriteToGenerator(
Asn1Generator ag,
Asn1Encodable ae)
{
byte[] encoded = ae.GetEncoded();
ag.GetRawOutputStream().Write(encoded, 0, encoded.Length);
}
}
}
}

View File

@@ -0,0 +1,240 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.CryptoPro;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Oiw;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.TeleTrust;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
public class CmsSignedGenerator
{
/**
* Default type for the signed data.
*/
public static readonly string Data = PkcsObjectIdentifiers.Data.Id;
public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id;
public static readonly string DigestSha224 = NistObjectIdentifiers.IdSha224.Id;
public static readonly string DigestSha256 = NistObjectIdentifiers.IdSha256.Id;
public static readonly string DigestSha384 = NistObjectIdentifiers.IdSha384.Id;
public static readonly string DigestSha512 = NistObjectIdentifiers.IdSha512.Id;
public static readonly string DigestMD5 = PkcsObjectIdentifiers.MD5.Id;
public static readonly string DigestGost3411 = CryptoProObjectIdentifiers.GostR3411.Id;
public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id;
public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id;
public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id;
public static readonly string EncryptionDsa = X9ObjectIdentifiers.IdDsaWithSha1.Id;
public static readonly string EncryptionECDsa = X9ObjectIdentifiers.ECDsaWithSha1.Id;
public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id;
public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id;
public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id;
private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id;
private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id;
private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id;
private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id;
private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id;
private static readonly ISet noParams = new HashSet();
private static readonly Hashtable ecAlgorithms = new Hashtable();
static CmsSignedGenerator()
{
noParams.Add(EncryptionDsa);
// noParams.Add(EncryptionECDsa);
noParams.Add(EncryptionECDsaWithSha1);
noParams.Add(EncryptionECDsaWithSha224);
noParams.Add(EncryptionECDsaWithSha256);
noParams.Add(EncryptionECDsaWithSha384);
noParams.Add(EncryptionECDsaWithSha512);
ecAlgorithms.Add(DigestSha1, EncryptionECDsaWithSha1);
ecAlgorithms.Add(DigestSha224, EncryptionECDsaWithSha224);
ecAlgorithms.Add(DigestSha256, EncryptionECDsaWithSha256);
ecAlgorithms.Add(DigestSha384, EncryptionECDsaWithSha384);
ecAlgorithms.Add(DigestSha512, EncryptionECDsaWithSha512);
}
internal ArrayList _certs = new ArrayList();
internal ArrayList _crls = new ArrayList();
internal ArrayList _signers = new ArrayList();
internal IDictionary _digests = new Hashtable();
protected readonly SecureRandom rand;
protected CmsSignedGenerator()
: this(new SecureRandom())
{
}
/// <summary>Constructor allowing specific source of randomness</summary>
/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
protected CmsSignedGenerator(
SecureRandom rand)
{
this.rand = rand;
}
protected string GetEncOid(
AsymmetricKeyParameter key,
string digestOID)
{
string encOID = null;
if (key is RsaKeyParameters)
{
if (!((RsaKeyParameters) key).IsPrivate)
throw new ArgumentException("Expected RSA private key");
encOID = EncryptionRsa;
}
else if (key is DsaPrivateKeyParameters)
{
if (!digestOID.Equals(DigestSha1))
throw new ArgumentException("can't mix DSA with anything but SHA1");
encOID = EncryptionDsa;
}
else if (key is ECPrivateKeyParameters)
{
ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) key;
string algName = ecPrivKey.AlgorithmName;
if (algName == "ECGOST3410")
{
encOID = EncryptionECGost3410;
}
else
{
// TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does?
encOID = (string) ecAlgorithms[digestOID];
if (encOID == null)
throw new ArgumentException("can't mix ECDSA with anything but SHA family digests");
}
}
else if (key is Gost3410PrivateKeyParameters)
{
encOID = EncryptionGost3410;
}
else
{
throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid");
}
return encOID;
}
internal static AlgorithmIdentifier GetEncAlgorithmIdentifier(
string encOid)
{
if (noParams.Contains(encOid))
{
return new AlgorithmIdentifier(new DerObjectIdentifier(encOid));
}
return new AlgorithmIdentifier(new DerObjectIdentifier(encOid), DerNull.Instance);
}
internal protected virtual IDictionary GetBaseParameters(
DerObjectIdentifier contentType,
AlgorithmIdentifier digAlgId,
byte[] hash)
{
IDictionary param = new Hashtable();
param[CmsAttributeTableParameter.ContentType] = contentType;
param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId;
if (hash != null)
{
param[CmsAttributeTableParameter.Digest] = hash.Clone();
}
return param;
}
internal protected virtual Asn1Set GetAttributeSet(
Asn1.Cms.AttributeTable attr)
{
return attr == null
? null
: new DerSet(attr.ToAsn1EncodableVector());
}
public void AddCertificates(
IX509Store certStore)
{
_certs.AddRange(CmsUtilities.GetCertificatesFromStore(certStore));
}
public void AddCrls(
IX509Store crlStore)
{
_crls.AddRange(CmsUtilities.GetCrlsFromStore(crlStore));
}
/**
* Add the attribute certificates contained in the passed in store to the
* generator.
*
* @param store a store of Version 2 attribute certificates
* @throws CmsException if an error occurse processing the store.
*/
public void AddAttributeCertificates(
IX509Store store)
{
try
{
foreach (IX509AttributeCertificate attrCert in store.GetMatches(null))
{
_certs.Add(new DerTaggedObject(false, 2,
AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded()))));
}
}
catch (Exception e)
{
throw new CmsException("error processing attribute certs", e);
}
}
/**
* Add a store of precalculated signers to the generator.
*
* @param signerStore store of signers
*/
public void AddSigners(
SignerInformationStore signerStore)
{
foreach (object o in signerStore.GetSigners())
{
_signers.Add(o);
}
}
/**
* Return a map of oids and byte arrays representing the digests calculated on the content during
* the last generate.
*
* @return a map of oids (as String objects) and byte[] representing digests.
*/
public IDictionary GetGeneratedDigests()
{
return new Hashtable(_digests);
}
}
}

View File

@@ -0,0 +1,332 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.CryptoPro;
using Org.BouncyCastle.Asn1.Iana;
using Org.BouncyCastle.Asn1.Misc;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Oiw;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.TeleTrust;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
internal class CmsSignedHelper
{
internal static readonly CmsSignedHelper Instance = new CmsSignedHelper();
private static readonly Hashtable encryptionAlgs = new Hashtable();
private static readonly Hashtable digestAlgs = new Hashtable();
private static readonly Hashtable digestAliases = new Hashtable();
private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption)
{
string alias = oid.Id;
digestAlgs.Add(alias, digest);
encryptionAlgs.Add(alias, encryption);
}
static CmsSignedHelper()
{
AddEntries(NistObjectIdentifiers.DsaWithSha224, "SHA224", "DSA");
AddEntries(NistObjectIdentifiers.DsaWithSha256, "SHA256", "DSA");
AddEntries(NistObjectIdentifiers.DsaWithSha384, "SHA384", "DSA");
AddEntries(NistObjectIdentifiers.DsaWithSha512, "SHA512", "DSA");
AddEntries(OiwObjectIdentifiers.DsaWithSha1, "SHA1", "DSA");
AddEntries(OiwObjectIdentifiers.MD4WithRsa, "MD4", "RSA");
AddEntries(OiwObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA");
AddEntries(OiwObjectIdentifiers.MD5WithRsa, "MD5", "RSA");
AddEntries(OiwObjectIdentifiers.Sha1WithRsa, "SHA1", "RSA");
AddEntries(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2", "RSA");
AddEntries(PkcsObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA");
AddEntries(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5", "RSA");
AddEntries(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1", "RSA");
AddEntries(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224", "RSA");
AddEntries(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256", "RSA");
AddEntries(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384", "RSA");
AddEntries(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512", "RSA");
AddEntries(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1", "ECDSA");
AddEntries(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224", "ECDSA");
AddEntries(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256", "ECDSA");
AddEntries(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384", "ECDSA");
AddEntries(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512", "ECDSA");
AddEntries(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1", "DSA");
encryptionAlgs.Add(X9ObjectIdentifiers.IdDsa.Id, "DSA");
encryptionAlgs.Add(PkcsObjectIdentifiers.RsaEncryption.Id, "RSA");
encryptionAlgs.Add(TeleTrusTObjectIdentifiers.TeleTrusTRsaSignatureAlgorithm, "RSA");
encryptionAlgs.Add(X509ObjectIdentifiers.IdEARsa.Id, "RSA");
encryptionAlgs.Add(CmsSignedGenerator.EncryptionRsaPss, "RSAandMGF1");
encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x94.Id, "GOST3410");
encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x2001.Id, "ECGOST3410");
encryptionAlgs.Add("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
encryptionAlgs.Add("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
digestAlgs.Add(PkcsObjectIdentifiers.MD2.Id, "MD2");
digestAlgs.Add(PkcsObjectIdentifiers.MD4.Id, "MD4");
digestAlgs.Add(PkcsObjectIdentifiers.MD5.Id, "MD5");
digestAlgs.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1");
digestAlgs.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224");
digestAlgs.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256");
digestAlgs.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384");
digestAlgs.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512");
digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128");
digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160");
digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256");
digestAlgs.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411");
digestAlgs.Add("1.3.6.1.4.1.5849.1.2.1", "GOST3411");
digestAliases.Add("SHA1", new string[] { "SHA-1" });
digestAliases.Add("SHA224", new string[] { "SHA-224" });
digestAliases.Add("SHA256", new string[] { "SHA-256" });
digestAliases.Add("SHA384", new string[] { "SHA-384" });
digestAliases.Add("SHA512", new string[] { "SHA-512" });
}
/**
* Return the digest algorithm using one of the standard JCA string
* representations rather than the algorithm identifier (if possible).
*/
internal string GetDigestAlgName(
string digestAlgOid)
{
string algName = (string)digestAlgs[digestAlgOid];
if (algName != null)
{
return algName;
}
return digestAlgOid;
}
internal string[] GetDigestAliases(
string algName)
{
string[] aliases = (string[]) digestAliases[algName];
return aliases == null ? new String[0] : (string[]) aliases.Clone();
}
/**
* Return the digest encryption algorithm using one of the standard
* JCA string representations rather than the algorithm identifier (if
* possible).
*/
internal string GetEncryptionAlgName(
string encryptionAlgOid)
{
string algName = (string) encryptionAlgs[encryptionAlgOid];
if (algName != null)
{
return algName;
}
return encryptionAlgOid;
}
internal IDigest GetDigestInstance(
string algorithm)
{
try
{
return DigestUtilities.GetDigest(algorithm);
}
catch (SecurityUtilityException e)
{
// This is probably superfluous on C#, since no provider infrastructure,
// assuming DigestUtilities already knows all the aliases
foreach (string alias in GetDigestAliases(algorithm))
{
try { return DigestUtilities.GetDigest(alias); }
catch (SecurityUtilityException) {}
}
throw e;
}
}
internal ISigner GetSignatureInstance(
string algorithm)
{
return SignerUtilities.GetSigner(algorithm);
}
internal IX509Store CreateAttributeStore(
string type,
Asn1Set certSet)
{
IList certs = new ArrayList();
if (certSet != null)
{
foreach (Asn1Encodable ae in certSet)
{
try
{
Asn1Object obj = ae.ToAsn1Object();
if (obj is Asn1TaggedObject)
{
Asn1TaggedObject tagged = (Asn1TaggedObject)obj;
if (tagged.TagNo == 2)
{
certs.Add(
new X509V2AttributeCertificate(
Asn1Sequence.GetInstance(tagged, false).GetEncoded()));
}
}
}
catch (Exception ex)
{
throw new CmsException("can't re-encode attribute certificate!", ex);
}
}
}
try
{
return X509StoreFactory.Create(
"AttributeCertificate/" + type,
new X509CollectionStoreParameters(certs));
}
catch (ArgumentException e)
{
throw new CmsException("can't setup the X509Store", e);
}
}
internal IX509Store CreateCertificateStore(
string type,
Asn1Set certSet)
{
IList certs = new ArrayList();
if (certSet != null)
{
AddCertsFromSet(certs, certSet);
}
try
{
return X509StoreFactory.Create(
"Certificate/" + type,
new X509CollectionStoreParameters(certs));
}
catch (ArgumentException e)
{
throw new CmsException("can't setup the X509Store", e);
}
}
internal IX509Store CreateCrlStore(
string type,
Asn1Set crlSet)
{
IList crls = new ArrayList();
if (crlSet != null)
{
AddCrlsFromSet(crls, crlSet);
}
try
{
return X509StoreFactory.Create(
"CRL/" + type,
new X509CollectionStoreParameters(crls));
}
catch (ArgumentException e)
{
throw new CmsException("can't setup the X509Store", e);
}
}
private void AddCertsFromSet(
IList certs,
Asn1Set certSet)
{
X509CertificateParser cf = new X509CertificateParser();
foreach (Asn1Encodable ae in certSet)
{
try
{
Asn1Object obj = ae.ToAsn1Object();
if (obj is Asn1Sequence)
{
// TODO Build certificate directly from sequence?
certs.Add(cf.ReadCertificate(obj.GetEncoded()));
}
}
catch (Exception ex)
{
throw new CmsException("can't re-encode certificate!", ex);
}
}
}
private void AddCrlsFromSet(
IList crls,
Asn1Set crlSet)
{
X509CrlParser cf = new X509CrlParser();
foreach (Asn1Encodable ae in crlSet)
{
try
{
// TODO Build CRL directly from ae.ToAsn1Object()?
crls.Add(cf.ReadCrl(ae.GetEncoded()));
}
catch (Exception ex)
{
throw new CmsException("can't re-encode CRL!", ex);
}
}
}
internal AlgorithmIdentifier FixAlgID(
AlgorithmIdentifier algId)
{
if (algId.Parameters == null)
return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
return algId;
}
private bool anyCertHasTypeOther()
{
// not supported
return false;
}
private bool anyCertHasV1Attribute()
{
// obsolete
return false;
}
private bool anyCertHasV2Attribute()
{
// TODO
return false;
}
private bool anyCrlHasTypeOther()
{
// not supported
return false;
}
}
}

View File

@@ -0,0 +1,88 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Cms
{
public class CmsTypedStream
{
private const int BufferSize = 32 * 1024;
private readonly string _oid;
private readonly Stream _in;
private readonly int _bufSize;
public CmsTypedStream(
Stream inStream)
: this(PkcsObjectIdentifiers.Data.Id, inStream, BufferSize)
{
}
public CmsTypedStream(
string oid,
Stream inStream)
: this(oid, inStream, BufferSize)
{
}
public CmsTypedStream(
string oid,
Stream inStream,
int bufSize)
{
_oid = oid;
_bufSize = bufSize;
_in = new FullReaderStream(inStream, bufSize);
}
public string ContentType
{
get { return _oid; }
}
public Stream ContentStream
{
get { return _in; }
}
public void Drain()
{
Streams.Drain(_in);
_in.Close();
}
private class FullReaderStream
: BaseInputStream
{
internal Stream _stream;
internal FullReaderStream(
Stream inStream,
int bufSize)
{
_stream = inStream;
}
public override int ReadByte()
{
return _stream.ReadByte();
}
public override int Read(
byte[] buf,
int off,
int len)
{
return Streams.ReadFully(_stream, buf, off, len);
}
public override void Close()
{
_stream.Close();
base.Close();
}
}
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
internal class CmsUtilities
{
// TODO Is there a .NET equivalent to this?
// private static readonly Runtime RUNTIME = Runtime.getRuntime();
internal static int MaximumMemory
{
get
{
// TODO Is there a .NET equivalent to this?
long maxMem = int.MaxValue;//RUNTIME.maxMemory();
if (maxMem > int.MaxValue)
{
return int.MaxValue;
}
return (int)maxMem;
}
}
internal static ContentInfo ReadContentInfo(
byte[] input)
{
// enforce limit checking as from a byte array
return ReadContentInfo(new Asn1InputStream(input));
}
internal static ContentInfo ReadContentInfo(
Stream input)
{
// enforce some limit checking
return ReadContentInfo(new Asn1InputStream(input, MaximumMemory));
}
private static ContentInfo ReadContentInfo(
Asn1InputStream aIn)
{
try
{
return ContentInfo.GetInstance(aIn.ReadObject());
}
catch (IOException e)
{
throw new CmsException("IOException reading content.", e);
}
catch (InvalidCastException e)
{
throw new CmsException("Malformed content.", e);
}
catch (ArgumentException e)
{
throw new CmsException("Malformed content.", e);
}
}
public static byte[] StreamToByteArray(
Stream inStream)
{
return Streams.ReadAll(inStream);
}
public static IList GetCertificatesFromStore(
IX509Store certStore)
{
try
{
IList certs = new ArrayList();
if (certStore != null)
{
foreach (X509Certificate c in certStore.GetMatches(null))
{
certs.Add(
X509CertificateStructure.GetInstance(
Asn1Object.FromByteArray(c.GetEncoded())));
}
}
return certs;
}
catch (CertificateEncodingException e)
{
throw new CmsException("error encoding certs", e);
}
catch (Exception e)
{
throw new CmsException("error processing certs", e);
}
}
public static IList GetCrlsFromStore(
IX509Store crlStore)
{
try
{
IList crls = new ArrayList();
if (crlStore != null)
{
foreach (X509Crl c in crlStore.GetMatches(null))
{
crls.Add(
CertificateList.GetInstance(
Asn1Object.FromByteArray(c.GetEncoded())));
}
}
return crls;
}
catch (CrlException e)
{
throw new CmsException("error encoding crls", e);
}
catch (Exception e)
{
throw new CmsException("error processing crls", e);
}
}
public static Asn1Set CreateBerSetFromList(
IList berObjects)
{
Asn1EncodableVector v = new Asn1EncodableVector();
foreach (Asn1Encodable ae in berObjects)
{
v.Add(ae);
}
return new BerSet(v);
}
public static Asn1Set CreateDerSetFromList(
IList derObjects)
{
Asn1EncodableVector v = new Asn1EncodableVector();
foreach (Asn1Encodable ae in derObjects)
{
v.Add(ae);
}
return new DerSet(v);
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Cms
{
internal class CounterSignatureDigestCalculator
: IDigestCalculator
{
private readonly string alg;
private readonly byte[] data;
internal CounterSignatureDigestCalculator(
string alg,
byte[] data)
{
this.alg = alg;
this.data = data;
}
public byte[] GetDigest()
{
IDigest digest = CmsSignedHelper.Instance.GetDigestInstance(alg);
digest.BlockUpdate(data, 0, data.Length);
return DigestUtilities.DoFinal(digest);
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
namespace Org.BouncyCastle.Cms
{
/**
* Default signed attributes generator.
*/
public class DefaultSignedAttributeTableGenerator
: CmsAttributeTableGenerator
{
private readonly Hashtable table;
/**
* Initialise to use all defaults
*/
public DefaultSignedAttributeTableGenerator()
{
table = new Hashtable();
}
/**
* Initialise with some extra attributes or overrides.
*
* @param attributeTable initial attribute table to use.
*/
public DefaultSignedAttributeTableGenerator(
AttributeTable attributeTable)
{
if (attributeTable != null)
{
table = attributeTable.ToHashtable();
}
else
{
table = new Hashtable();
}
}
/**
* Create a standard attribute table from the passed in parameters - this will
* normally include contentType, signingTime, and messageDigest. If the constructor
* using an AttributeTable was used, entries in it for contentType, signingTime, and
* messageDigest will override the generated ones.
*
* @param parameters source parameters for table generation.
*
* @return a filled in Hashtable of attributes.
*/
protected virtual Hashtable createStandardAttributeTable(
IDictionary parameters)
{
Hashtable std = (Hashtable)table.Clone();
if (!std.ContainsKey(CmsAttributes.ContentType))
{
Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.ContentType,
new DerSet((DerObjectIdentifier)parameters[CmsAttributeTableParameter.ContentType]));
std[attr.AttrType] = attr;
}
if (!std.ContainsKey(CmsAttributes.SigningTime))
{
Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(
CmsAttributes.SigningTime, new DerSet(new Time(DateTime.UtcNow)));
std[attr.AttrType] = attr;
}
if (!std.ContainsKey(CmsAttributes.MessageDigest))
{
byte[] hash = (byte[])parameters[CmsAttributeTableParameter.Digest];
Asn1.Cms.Attribute attr;
if (hash != null)
{
attr = new Asn1.Cms.Attribute(
CmsAttributes.MessageDigest, new DerSet(new DerOctetString(hash)));
}
else
{
attr = new Asn1.Cms.Attribute(
CmsAttributes.MessageDigest, new DerSet(DerNull.Instance));
}
std[attr.AttrType] = attr;
}
return std;
}
/**
* @param parameters source parameters
* @return the populated attribute table
*/
public virtual AttributeTable GetAttributes(
IDictionary parameters)
{
return new AttributeTable(createStandardAttributeTable(parameters));
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Org.BouncyCastle.Cms
{
internal interface IDigestCalculator
{
byte[] GetDigest();
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Cms
{
/**
* the RecipientInfo class for a recipient who has been sent a message
* encrypted using a secret key known to the other side.
*/
public class KekRecipientInformation
: RecipientInformation
{
private KekRecipientInfo _info;
// private AlgorithmIdentifier _encAlg;
public KekRecipientInformation(
KekRecipientInfo info,
AlgorithmIdentifier encAlg,
Stream data)
: base(encAlg, AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm), data)
{
this._info = info;
this._encAlg = encAlg;
this._rid = new RecipientID();
KekIdentifier kekId = info.KekID;
_rid.KeyIdentifier = kekId.KeyIdentifier.GetOctets();
}
/**
* decrypt the content and return an input stream.
*/
public override CmsTypedStream GetContentStream(
ICipherParameters key)
{
try
{
byte[] encryptedKey = _info.EncryptedKey.GetOctets();
IWrapper keyWrapper = WrapperUtilities.GetWrapper(_keyEncAlg.ObjectID.Id);
keyWrapper.Init(false, key);
KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
_encAlg.ObjectID, keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
return GetContentFromSessionKey(sKey);
}
catch (SecurityUtilityException e)
{
throw new CmsException("couldn't create cipher.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key invalid in message.", e);
}
}
}
}

View File

@@ -0,0 +1,122 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* the RecipientInfo class for a recipient who has been sent a message
* encrypted using key agreement.
*/
public class KeyAgreeRecipientInformation
: RecipientInformation
{
private KeyAgreeRecipientInfo _info;
// private AlgorithmIdentifier _encAlg;
private Asn1OctetString _encryptedKey;
public KeyAgreeRecipientInformation(
KeyAgreeRecipientInfo info,
AlgorithmIdentifier encAlg,
Stream data)
: base(encAlg, AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm), data)
{
_info = info;
// _encAlg = encAlg;
try
{
Asn1Sequence s = _info.RecipientEncryptedKeys;
RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(s[0]);
Asn1.Cms.IssuerAndSerialNumber iAnds = id.Identifier.IssuerAndSerialNumber;
// byte[] issuerBytes = iAnds.Name.GetEncoded();
_rid = new RecipientID();
// _rid.SetIssuer(issuerBytes);
_rid.Issuer = iAnds.Name;
_rid.SerialNumber = iAnds.SerialNumber.Value;
_encryptedKey = id.EncryptedKey;
}
catch (IOException e)
{
throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e);
}
}
/**
* decrypt the content and return an input stream.
*/
public override CmsTypedStream GetContentStream(
// Key key)
ICipherParameters key)
{
if (!(key is AsymmetricKeyParameter))
throw new ArgumentException("KeyAgreement requires asymmetric key", "key");
AsymmetricKeyParameter privKey = (AsymmetricKeyParameter) key;
if (!privKey.IsPrivate)
throw new ArgumentException("Expected private key", "key");
try
{
OriginatorPublicKey origK = _info.Originator.OriginatorKey;
PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey);
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(privInfo.AlgorithmID, origK.PublicKey.GetBytes());
AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(pubInfo);
string wrapAlg = DerObjectIdentifier.GetInstance(
Asn1Sequence.GetInstance(_keyEncAlg.Parameters)[0]).Id;
IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf(
_keyEncAlg.ObjectID, wrapAlg);
agreement.Init(privKey);
BigInteger wKeyNum = agreement.CalculateAgreement(pubKey);
// TODO Fix the way bytes are derived from the secret
byte[] wKeyBytes = wKeyNum.ToByteArrayUnsigned();
KeyParameter wKey = ParameterUtilities.CreateKeyParameter(wrapAlg, wKeyBytes);
IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg);
keyCipher.Init(false, wKey);
AlgorithmIdentifier aid = _encAlg;
string alg = aid.ObjectID.Id;
byte[] encryptedKey = _encryptedKey.GetOctets();
byte[] sKeyBytes = keyCipher.Unwrap(encryptedKey, 0, encryptedKey.Length);
KeyParameter sKey = ParameterUtilities.CreateKeyParameter(alg, sKeyBytes);
return GetContentFromSessionKey(sKey);
}
catch (SecurityUtilityException e)
{
throw new CmsException("couldn't create cipher.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key invalid in message.", e);
}
catch (Exception e)
{
throw new CmsException("originator key invalid.", e);
}
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Asn1Pkcs = Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* the KeyTransRecipientInformation class for a recipient who has been sent a secret
* key encrypted using their public key that needs to be used to
* extract the message.
*/
public class KeyTransRecipientInformation
: RecipientInformation
{
private KeyTransRecipientInfo _info;
// private new AlgorithmIdentifier _encAlg;
public KeyTransRecipientInformation(
KeyTransRecipientInfo info,
AlgorithmIdentifier encAlg,
Stream data)
: base(encAlg, AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm), data)
{
this._info = info;
// this._encAlg = encAlg;
this._rid = new RecipientID();
RecipientIdentifier r = info.RecipientIdentifier;
try
{
if (r.IsTagged)
{
Asn1OctetString octs = Asn1OctetString.GetInstance(r.ID);
_rid.SubjectKeyIdentifier = octs.GetOctets();
}
else
{
IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.GetInstance(r.ID);
_rid.Issuer = iAnds.Name;
_rid.SerialNumber = iAnds.SerialNumber.Value;
}
}
catch (IOException)
{
throw new ArgumentException("invalid rid in KeyTransRecipientInformation");
}
}
private string GetExchangeEncryptionAlgorithmName(
DerObjectIdentifier oid)
{
if (Asn1Pkcs.PkcsObjectIdentifiers.RsaEncryption.Equals(oid))
{
return "RSA//PKCS1Padding";
}
return oid.Id;
}
/**
* decrypt the content and return it as a byte array.
*/
public override CmsTypedStream GetContentStream(
ICipherParameters key)
{
byte[] encryptedKey = _info.EncryptedKey.GetOctets();
string keyExchangeAlgorithm = GetExchangeEncryptionAlgorithmName(_keyEncAlg.ObjectID);
try
{
IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyExchangeAlgorithm);
keyWrapper.Init(false, key);
KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
_encAlg.ObjectID, keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
return GetContentFromSessionKey(sKey);
}
catch (SecurityUtilityException e)
{
throw new CmsException("couldn't create cipher.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key invalid in message.", e);
}
// catch (IllegalBlockSizeException e)
catch (DataLengthException e)
{
throw new CmsException("illegal blocksize in message.", e);
}
// catch (BadPaddingException e)
catch (InvalidCipherTextException e)
{
throw new CmsException("bad padding in message.", e);
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Cms
{
public class Pkcs5Scheme2PbeKey
: CmsPbeKey
{
public Pkcs5Scheme2PbeKey(
string password,
byte[] salt,
int iterationCount)
: base(password, salt, iterationCount)
{
}
internal override KeyParameter GetEncoded(
string algorithmOid)
{
Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator();
gen.Init(
PbeParametersGenerator.Pkcs5PasswordToBytes(this.Password),
this.Salt,
this.IterationCount);
return (KeyParameter) gen.GenerateDerivedParameters(
algorithmOid,
CmsEnvelopedHelper.Instance.GetKeySize(algorithmOid));
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
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.Parameters;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Cms
{
/**
* the RecipientInfo class for a recipient who has been sent a message
* encrypted using a password.
*/
public class PasswordRecipientInformation
: RecipientInformation
{
private readonly PasswordRecipientInfo _info;
// private readonly AlgorithmIdentifier _encAlg;
public PasswordRecipientInformation(
PasswordRecipientInfo info,
AlgorithmIdentifier encAlg,
Stream data)
: base(encAlg, AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm), data)
{
this._info = info;
// this._encAlg = encAlg;
this._rid = new RecipientID();
}
/**
* decrypt the content and return an input stream.
*/
public override CmsTypedStream GetContentStream(
ICipherParameters key)
{
try
{
AlgorithmIdentifier kekAlg = AlgorithmIdentifier.GetInstance(_info.KeyEncryptionAlgorithm);
Asn1Sequence kekAlgParams = (Asn1Sequence)kekAlg.Parameters;
byte[] encryptedKey = _info.EncryptedKey.GetOctets();
string kekAlgName = DerObjectIdentifier.GetInstance(kekAlgParams[0]).Id;
string cName = CmsEnvelopedHelper.Instance.GetRfc3211WrapperName(kekAlgName);
IWrapper keyWrapper = WrapperUtilities.GetWrapper(cName);
byte[] iv = Asn1OctetString.GetInstance(kekAlgParams[1]).GetOctets();
ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName);
parameters = new ParametersWithIV(parameters, iv);
keyWrapper.Init(false, parameters);
AlgorithmIdentifier aid = _encAlg;
string alg = aid.ObjectID.Id;
KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
alg, keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
return GetContentFromSessionKey(sKey);
}
catch (SecurityUtilityException e)
{
throw new CmsException("couldn't create cipher.", e);
}
catch (InvalidKeyException e)
{
throw new CmsException("key invalid in message.", e);
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
public class RecipientID
: X509CertStoreSelector
{
private byte[] keyIdentifier;
public byte[] KeyIdentifier
{
get { return Arrays.Clone(keyIdentifier); }
set { keyIdentifier = Arrays.Clone(value); }
}
public override int GetHashCode()
{
int code = Arrays.GetHashCode(keyIdentifier)
^ Arrays.GetHashCode(this.SubjectKeyIdentifier);
BigInteger serialNumber = this.SerialNumber;
if (serialNumber != null)
{
code ^= serialNumber.GetHashCode();
}
string issuer = this.IssuerAsString;
if (issuer != null)
{
code ^= issuer.GetHashCode();
}
return code;
}
public override bool Equals(
object obj)
{
if (obj == this)
return true;
RecipientID id = obj as RecipientID;
if (id == null)
return false;
return Arrays.AreEqual(keyIdentifier, id.keyIdentifier)
&& Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier)
&& Platform.Equals(SerialNumber, id.SerialNumber)
&& Platform.Equals(IssuerAsString, id.IssuerAsString);
}
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Pkcs;
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.Collections;
namespace Org.BouncyCastle.Cms
{
public abstract class RecipientInformation
{
internal RecipientID _rid = new RecipientID();
internal AlgorithmIdentifier _encAlg;
internal AlgorithmIdentifier _keyEncAlg;
internal Stream _data;
internal RecipientInformation(
AlgorithmIdentifier encAlg,
AlgorithmIdentifier keyEncAlg,
Stream data)
{
if (!data.CanRead)
throw new ArgumentException("Expected input stream", "data");
this._encAlg = encAlg;
this._keyEncAlg = keyEncAlg;
this._data = data;
}
public RecipientID RecipientID
{
get { return _rid; }
}
public AlgorithmIdentifier KeyEncryptionAlgorithmID
{
get { return _keyEncAlg; }
}
/**
* return the object identifier for the key encryption algorithm.
* @return OID for key encryption algorithm.
*/
public string KeyEncryptionAlgOid
{
get { return _keyEncAlg.ObjectID.Id; }
}
/**
* return the ASN.1 encoded key encryption algorithm parameters, or null if
* there aren't any.
* @return ASN.1 encoding of key encryption algorithm parameters.
*/
public Asn1Object KeyEncryptionAlgParams
{
get
{
Asn1Encodable ae = _keyEncAlg.Parameters;
return ae == null ? null : ae.ToAsn1Object();
}
}
internal CmsTypedStream GetContentFromSessionKey(
KeyParameter sKey)
{
try
{
IBufferedCipher cipher = CipherUtilities.GetCipher(_encAlg.ObjectID);
Asn1Encodable asn1Enc = _encAlg.Parameters;
Asn1Object asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object();
ICipherParameters cipherParameters = sKey;
if (asn1Params != null && !(asn1Params is Asn1Null))
{
cipherParameters = ParameterUtilities.GetCipherParameters(
_encAlg.ObjectID, cipherParameters, asn1Params);
}
else
{
string alg = _encAlg.ObjectID.Id;
if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc)
|| alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc)
|| alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc))
{
cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]);
}
}
cipher.Init(false, cipherParameters);
return new CmsTypedStream(new CipherStream(_data, cipher, null));
}
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("error decoding algorithm parameters.", e);
}
}
public byte[] GetContent(
ICipherParameters key)
{
try
{
if (_data is MemoryStream)
{
// _data.Reset();
_data.Seek(0L, SeekOrigin.Begin);
}
return CmsUtilities.StreamToByteArray(GetContentStream(key).ContentStream);
}
catch (IOException e)
{
throw new Exception("unable to parse internal stream: " + e);
}
}
public abstract CmsTypedStream GetContentStream(ICipherParameters key);
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections;
namespace Org.BouncyCastle.Cms
{
public class RecipientInformationStore
{
private readonly ArrayList all; //ArrayList[RecipientInformation]
private readonly Hashtable table = new Hashtable(); // Hashtable[RecipientID, ArrayList[RecipientInformation]]
public RecipientInformationStore(
ICollection recipientInfos)
{
foreach (RecipientInformation recipientInformation in recipientInfos)
{
RecipientID rid = recipientInformation.RecipientID;
ArrayList list = (ArrayList) table[rid];
if (list == null)
{
table[rid] = list = new ArrayList(1);
}
list.Add(recipientInformation);
}
this.all = new ArrayList(recipientInfos);
}
/**
* Return the first RecipientInformation object that matches the
* passed in selector. Null if there are no matches.
*
* @param selector to identify a recipient
* @return a single RecipientInformation object. Null if none matches.
*/
public RecipientInformation GetFirstRecipient(
RecipientID selector)
{
ArrayList list = (ArrayList) table[selector];
return list == null ? null : (RecipientInformation) list[0];
}
/**
* Return the number of recipients in the collection.
*
* @return number of recipients identified.
*/
public int Count
{
get { return all.Count; }
}
/**
* Return all recipients in the collection
*
* @return a collection of recipients.
*/
public ICollection GetRecipients()
{
return new ArrayList(all);
}
/**
* Return possible empty collection with recipients matching the passed in RecipientID
*
* @param selector a recipient id to select against.
* @return a collection of RecipientInformation objects.
*/
public ICollection GetRecipients(
RecipientID selector)
{
ArrayList list = (ArrayList) table[selector];
return list == null ? new ArrayList() : new ArrayList(list);
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509.Store;
namespace Org.BouncyCastle.Cms
{
/**
* a basic index for a signer.
*/
public class SignerID
: X509CertStoreSelector
{
public override int GetHashCode()
{
int code = Arrays.GetHashCode(this.SubjectKeyIdentifier);
BigInteger serialNumber = this.SerialNumber;
if (serialNumber != null)
{
code ^= serialNumber.GetHashCode();
}
string issuer = this.IssuerAsString;
if (issuer != null)
{
code ^= issuer.GetHashCode();
}
return code;
}
public override bool Equals(
object obj)
{
if (obj == this)
return false;
SignerID id = obj as SignerID;
if (id == null)
return false;
return Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier)
&& Platform.Equals(SerialNumber, id.SerialNumber)
&& Platform.Equals(IssuerAsString, id.IssuerAsString);
}
}
}

View File

@@ -0,0 +1,631 @@
using System;
using System.Collections;
using System.Diagnostics;
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.Engines;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Cms
{
/**
* an expanded SignerInfo block from a CMS Signed message
*/
public class SignerInformation
{
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
private SignerID sid;
private SignerInfo info;
private AlgorithmIdentifier digestAlgorithm;
private AlgorithmIdentifier encryptionAlgorithm;
private Asn1Set signedAttributes;
private Asn1Set unsignedAttributes;
private CmsProcessable content;
private byte[] signature;
private DerObjectIdentifier contentType;
private IDigestCalculator digestCalculator;
private byte[] resultDigest;
internal SignerInformation(
SignerInfo info,
DerObjectIdentifier contentType,
CmsProcessable content,
IDigestCalculator digestCalculator)
{
this.info = info;
this.sid = new SignerID();
this.contentType = contentType;
try
{
SignerIdentifier s = info.SignerID;
if (s.IsTagged)
{
Asn1OctetString octs = Asn1OctetString.GetInstance(s.ID);
sid.SubjectKeyIdentifier = octs.GetOctets();
}
else
{
Asn1.Cms.IssuerAndSerialNumber iAnds =
Asn1.Cms.IssuerAndSerialNumber.GetInstance(s.ID);
sid.Issuer = iAnds.Name;
sid.SerialNumber = iAnds.SerialNumber.Value;
}
}
catch (IOException)
{
throw new ArgumentException("invalid sid in SignerInfo");
}
this.digestAlgorithm = info.DigestAlgorithm;
this.signedAttributes = info.AuthenticatedAttributes;
this.unsignedAttributes = info.UnauthenticatedAttributes;
this.encryptionAlgorithm = info.DigestEncryptionAlgorithm;
this.signature = info.EncryptedDigest.GetOctets();
this.content = content;
this.digestCalculator = digestCalculator;
}
public SignerID SignerID
{
get { return sid; }
}
/**
* return the version number for this objects underlying SignerInfo structure.
*/
public int Version
{
get { return info.Version.Value.IntValue; }
}
public AlgorithmIdentifier DigestAlgorithmID
{
get { return digestAlgorithm; }
}
/**
* return the object identifier for the signature.
*/
public string DigestAlgOid
{
get { return digestAlgorithm.ObjectID.Id; }
}
/**
* return the signature parameters, or null if there aren't any.
*/
public Asn1Object DigestAlgParams
{
get
{
Asn1Encodable ae = digestAlgorithm.Parameters;
return ae == null ? null : ae.ToAsn1Object();
}
}
/**
* return the content digest that was calculated during verification.
*/
public byte[] GetContentDigest()
{
if (resultDigest == null)
{
throw new InvalidOperationException("method can only be called after verify.");
}
return (byte[])resultDigest.Clone();
}
public AlgorithmIdentifier EncryptionAlgorithmID
{
get { return encryptionAlgorithm; }
}
/**
* return the object identifier for the signature.
*/
public string EncryptionAlgOid
{
get { return encryptionAlgorithm.ObjectID.Id; }
}
/**
* return the signature/encryption algorithm parameters, or null if
* there aren't any.
*/
public Asn1Object EncryptionAlgParams
{
get
{
Asn1Encodable ae = encryptionAlgorithm.Parameters;
return ae == null ? null : ae.ToAsn1Object();
}
}
/**
* return a table of the signed attributes - indexed by
* the OID of the attribute.
*/
public Asn1.Cms.AttributeTable SignedAttributes
{
get
{
return signedAttributes == null
? null
: new Asn1.Cms.AttributeTable(signedAttributes);
}
}
/**
* return a table of the unsigned attributes indexed by
* the OID of the attribute.
*/
public Asn1.Cms.AttributeTable UnsignedAttributes
{
get
{
return unsignedAttributes == null
? null
: new Asn1.Cms.AttributeTable(unsignedAttributes);
}
}
/**
* return the encoded signature
*/
public byte[] GetSignature()
{
return (byte[]) signature.Clone();
}
/**
* Return a SignerInformationStore containing the counter signatures attached to this
* signer. If no counter signatures are present an empty store is returned.
*/
public SignerInformationStore GetCounterSignatures()
{
Asn1.Cms.AttributeTable unsignedAttributeTable = UnsignedAttributes;
if (unsignedAttributeTable == null)
{
return new SignerInformationStore(new ArrayList(0));
}
IList counterSignatures = new ArrayList();
Asn1.Cms.Attribute counterSignatureAttribute = unsignedAttributeTable[CmsAttributes.CounterSignature];
if (counterSignatureAttribute != null)
{
Asn1Set values = counterSignatureAttribute.AttrValues;
counterSignatures = new ArrayList(values.Count);
foreach (Asn1Encodable asn1Obj in values)
{
SignerInfo si = SignerInfo.GetInstance(asn1Obj.ToAsn1Object());
string digestName = CmsSignedHelper.Instance.GetDigestAlgName(si.DigestAlgorithm.ObjectID.Id);
counterSignatures.Add(new SignerInformation(si, CmsAttributes.CounterSignature, null, new CounterSignatureDigestCalculator(digestName, GetSignature())));
}
}
return new SignerInformationStore(counterSignatures);
}
/**
* return the DER encoding of the signed attributes.
* @throws IOException if an encoding error occurs.
*/
public byte[] GetEncodedSignedAttributes()
{
return signedAttributes == null
? null
: signedAttributes.GetEncoded(Asn1Encodable.Der);
}
private bool DoVerify(
AsymmetricKeyParameter key,
Asn1.Cms.AttributeTable signedAttrTable)
{
string digestName = Helper.GetDigestAlgName(this.DigestAlgOid);
IDigest digest = Helper.GetDigestInstance(digestName);
DerObjectIdentifier sigAlgOid = this.encryptionAlgorithm.ObjectID;
Asn1Encodable sigParams = this.encryptionAlgorithm.Parameters;
ISigner sig;
if (sigAlgOid.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdRsassaPss))
{
// RFC 4056
// When the id-RSASSA-PSS algorithm identifier is used for a signature,
// the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
if (sigParams == null)
throw new CmsException("RSASSA-PSS signature must specify algorithm parameters");
try
{
// TODO Provide abstract configuration mechanism
Asn1.Pkcs.RsassaPssParameters pss = Asn1.Pkcs.RsassaPssParameters.GetInstance(
sigParams.ToAsn1Object());
if (!pss.HashAlgorithm.ObjectID.Equals(this.digestAlgorithm.ObjectID))
throw new CmsException("RSASSA-PSS signature parameters specified incorrect hash algorithm");
if (!pss.MaskGenAlgorithm.ObjectID.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdMgf1))
throw new CmsException("RSASSA-PSS signature parameters specified unknown MGF");
IDigest pssDigest = DigestUtilities.GetDigest(pss.HashAlgorithm.ObjectID);
int saltLen = pss.SaltLength.Value.IntValue;
byte trailer = (byte) pss.TrailerField.Value.IntValue;
sig = new Crypto.Signers.PssSigner(new RsaBlindedEngine(), pssDigest, saltLen, trailer);
}
catch (Exception e)
{
throw new CmsException("failed to set RSASSA-PSS signature parameters", e);
}
}
else
{
// TODO Probably too strong a check at the moment
// if (sigParams != null)
// throw new CmsException("unrecognised signature parameters provided");
string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
sig = Helper.GetSignatureInstance(signatureName);
}
try
{
sig.Init(false, key);
if (signedAttributes == null)
{
if (content != null)
{
content.Write(new CmsSignedDataGenerator.SigOutputStream(sig));
content.Write(new CmsSignedDataGenerator.DigOutputStream(digest));
resultDigest = DigestUtilities.DoFinal(digest);
}
else
{
resultDigest = digestCalculator.GetDigest();
// need to decrypt signature and check message bytes
return VerifyDigest(resultDigest, key, this.GetSignature());
}
}
else
{
byte[] hash;
if (content != null)
{
content.Write(
new CmsSignedDataGenerator.DigOutputStream(digest));
hash = DigestUtilities.DoFinal(digest);
}
else if (digestCalculator != null)
{
hash = digestCalculator.GetDigest();
}
else
{
hash = null;
}
resultDigest = hash;
Asn1.Cms.Attribute dig = signedAttrTable[Asn1.Cms.CmsAttributes.MessageDigest];
Asn1.Cms.Attribute type = signedAttrTable[Asn1.Cms.CmsAttributes.ContentType];
if (dig == null)
{
throw new SignatureException("no hash for content found in signed attributes");
}
if (type == null && !contentType.Equals(CmsAttributes.CounterSignature))
{
throw new SignatureException("no content type id found in signed attributes");
}
Asn1Object hashObj = dig.AttrValues[0].ToAsn1Object();
if (hashObj is Asn1OctetString)
{
byte[] signedHash = ((Asn1OctetString)hashObj).GetOctets();
if (!Arrays.AreEqual(hash, signedHash))
{
throw new SignatureException("content hash found in signed attributes different");
}
}
else if (hashObj is DerNull)
{
if (hash != null)
{
throw new SignatureException("NULL hash found in signed attributes when one expected");
}
}
if (type != null)
{
DerObjectIdentifier typeOID = (DerObjectIdentifier)type.AttrValues[0];
if (!typeOID.Equals(contentType))
{
throw new SignatureException("contentType in signed attributes different");
}
}
byte[] tmp = this.GetEncodedSignedAttributes();
sig.BlockUpdate(tmp, 0, tmp.Length);
}
return sig.VerifySignature(this.GetSignature());
}
catch (InvalidKeyException e)
{
throw new CmsException(
"key not appropriate to signature in message.", e);
}
catch (IOException e)
{
throw new CmsException(
"can't process mime object to create signature.", e);
}
catch (SignatureException e)
{
throw new CmsException(
"invalid signature format in message: " + e.Message, e);
}
}
private bool IsNull(
Asn1Encodable o)
{
return (o is Asn1Null) || (o == null);
}
private DigestInfo DerDecode(
byte[] encoding)
{
if (encoding[0] != (int)(Asn1Tags.Constructed | Asn1Tags.Sequence))
{
throw new IOException("not a digest info object");
}
DigestInfo digInfo = DigestInfo.GetInstance(Asn1Object.FromByteArray(encoding));
// length check to avoid Bleichenbacher vulnerability
if (digInfo.GetEncoded().Length != encoding.Length)
{
throw new CmsException("malformed RSA signature");
}
return digInfo;
}
private bool VerifyDigest(
byte[] digest,
AsymmetricKeyParameter key,
byte[] signature)
{
string algorithm = Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
try
{
if (algorithm.Equals("RSA"))
{
IBufferedCipher c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
c.Init(false, key);
byte[] decrypt = c.DoFinal(signature);
DigestInfo digInfo = DerDecode(decrypt);
if (!digInfo.AlgorithmID.ObjectID.Equals(digestAlgorithm.ObjectID))
{
return false;
}
if (!IsNull(digInfo.AlgorithmID.Parameters))
{
return false;
}
byte[] sigHash = digInfo.GetDigest();
return Arrays.AreEqual(digest, sigHash);
}
else if (algorithm.Equals("DSA"))
{
ISigner sig = SignerUtilities.GetSigner("NONEwithDSA");
sig.Init(false, key);
sig.BlockUpdate(digest, 0, digest.Length);
return sig.VerifySignature(signature);
}
else
{
throw new CmsException("algorithm: " + algorithm + " not supported in base signatures.");
}
}
catch (SecurityUtilityException e)
{
throw e;
}
catch (GeneralSecurityException e)
{
throw new CmsException("Exception processing signature: " + e, e);
}
catch (IOException e)
{
throw new CmsException("Exception decoding signature: " + e, e);
}
}
/**
* verify that the given public key succesfully handles and confirms the
* signature associated with this signer.
*/
public bool Verify(
AsymmetricKeyParameter pubKey)
{
if (pubKey.IsPrivate)
throw new ArgumentException("Expected public key", "pubKey");
return DoVerify(pubKey, this.SignedAttributes);
}
/**
* verify that the given certificate successfully handles and confirms
* the signature associated with this signer and, if a signingTime
* attribute is available, that the certificate was valid at the time the
* signature was generated.
*/
public bool Verify(
X509Certificate cert)
{
Asn1.Cms.AttributeTable attr = this.SignedAttributes;
if (attr != null)
{
Asn1EncodableVector v = attr.GetAll(CmsAttributes.SigningTime);
switch (v.Count)
{
case 0:
break;
case 1:
{
Asn1.Cms.Attribute t = (Asn1.Cms.Attribute) v[0];
Debug.Assert(t != null);
Asn1Set attrValues = t.AttrValues;
if (attrValues.Count != 1)
throw new CmsException("A signing-time attribute MUST have a single attribute value");
Asn1.Cms.Time time = Asn1.Cms.Time.GetInstance(attrValues[0].ToAsn1Object());
cert.CheckValidity(time.Date);
break;
}
default:
throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the signing-time attribute");
}
}
return DoVerify(cert.GetPublicKey(), attr);
}
/**
* Return the base ASN.1 CMS structure that this object contains.
*
* @return an object containing a CMS SignerInfo structure.
*/
public SignerInfo ToSignerInfo()
{
return info;
}
/**
* Return a signer information object with the passed in unsigned
* attributes replacing the ones that are current associated with
* the object passed in.
*
* @param signerInformation the signerInfo to be used as the basis.
* @param unsignedAttributes the unsigned attributes to add.
* @return a copy of the original SignerInformationObject with the changed attributes.
*/
public static SignerInformation ReplaceUnsignedAttributes(
SignerInformation signerInformation,
Asn1.Cms.AttributeTable unsignedAttributes)
{
SignerInfo sInfo = signerInformation.info;
Asn1Set unsignedAttr = null;
if (unsignedAttributes != null)
{
unsignedAttr = new DerSet(unsignedAttributes.ToAsn1EncodableVector());
}
return new SignerInformation(
new SignerInfo(
sInfo.SignerID,
sInfo.DigestAlgorithm,
sInfo.AuthenticatedAttributes,
sInfo.DigestEncryptionAlgorithm,
sInfo.EncryptedDigest,
unsignedAttr),
signerInformation.contentType,
signerInformation.content,
null);
}
/**
* Return a signer information object with passed in SignerInformationStore representing counter
* signatures attached as an unsigned attribute.
*
* @param signerInformation the signerInfo to be used as the basis.
* @param counterSigners signer info objects carrying counter signature.
* @return a copy of the original SignerInformationObject with the changed attributes.
*/
public static SignerInformation AddCounterSigners(
SignerInformation signerInformation,
SignerInformationStore counterSigners)
{
SignerInfo sInfo = signerInformation.info;
Asn1.Cms.AttributeTable unsignedAttr = signerInformation.UnsignedAttributes;
Asn1EncodableVector v;
if (unsignedAttr != null)
{
v = unsignedAttr.ToAsn1EncodableVector();
}
else
{
v = new Asn1EncodableVector();
}
Asn1EncodableVector sigs = new Asn1EncodableVector();
foreach (SignerInformation sigInf in counterSigners.GetSigners())
{
sigs.Add(sigInf.ToSignerInfo());
}
v.Add(new Asn1.Cms.Attribute(CmsAttributes.CounterSignature, new DerSet(sigs)));
return new SignerInformation(
new SignerInfo(
sInfo.SignerID,
sInfo.DigestAlgorithm,
sInfo.AuthenticatedAttributes,
sInfo.DigestEncryptionAlgorithm,
sInfo.EncryptedDigest,
new DerSet(v)),
signerInformation.contentType,
signerInformation.content,
null);
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections;
using System.IO;
namespace Org.BouncyCastle.Cms
{
public class SignerInformationStore
{
private readonly ArrayList all; //ArrayList[SignerInformation]
private readonly Hashtable table = new Hashtable(); // Hashtable[SignerID, ArrayList[SignerInformation]]
public SignerInformationStore(
ICollection signerInfos)
{
foreach (SignerInformation signer in signerInfos)
{
SignerID sid = signer.SignerID;
ArrayList list = (ArrayList) table[sid];
if (list == null)
{
table[sid] = list = new ArrayList(1);
}
list.Add(signer);
}
this.all = new ArrayList(signerInfos);
}
/**
* Return the first SignerInformation object that matches the
* passed in selector. Null if there are no matches.
*
* @param selector to identify a signer
* @return a single SignerInformation object. Null if none matches.
*/
public SignerInformation GetFirstSigner(
SignerID selector)
{
ArrayList list = (ArrayList) table[selector];
return list == null ? null : (SignerInformation) list[0];
}
/// <summary>The number of signers in the collection.</summary>
public int Count
{
get { return all.Count; }
}
/// <returns>An ICollection of all signers in the collection</returns>
public ICollection GetSigners()
{
return new ArrayList(all);
}
/**
* Return possible empty collection with signers matching the passed in SignerID
*
* @param selector a signer id to select against.
* @return a collection of SignerInformation objects.
*/
public ICollection GetSigners(
SignerID selector)
{
ArrayList list = (ArrayList) table[selector];
return list == null ? new ArrayList() : new ArrayList(list);
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1.Cms;
namespace Org.BouncyCastle.Cms
{
/**
* Basic generator that just returns a preconstructed attribute table
*/
public class SimpleAttributeTableGenerator
: CmsAttributeTableGenerator
{
private readonly AttributeTable attributes;
public SimpleAttributeTableGenerator(
AttributeTable attributes)
{
this.attributes = attributes;
}
public virtual AttributeTable GetAttributes(
IDictionary parameters)
{
return attributes;
}
}
}