Initial Commit
This commit is contained in:
23
iTechSharp/srcbc/cms/BaseDigestCalculator.cs
Normal file
23
iTechSharp/srcbc/cms/BaseDigestCalculator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
25
iTechSharp/srcbc/cms/CMSAttributeTableGenerationException.cs
Normal file
25
iTechSharp/srcbc/cms/CMSAttributeTableGenerationException.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
25
iTechSharp/srcbc/cms/CMSAttributeTableGenerator.cs
Normal file
25
iTechSharp/srcbc/cms/CMSAttributeTableGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
73
iTechSharp/srcbc/cms/CMSCompressedData.cs
Normal file
73
iTechSharp/srcbc/cms/CMSCompressedData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
69
iTechSharp/srcbc/cms/CMSCompressedDataGenerator.cs
Normal file
69
iTechSharp/srcbc/cms/CMSCompressedDataGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
57
iTechSharp/srcbc/cms/CMSCompressedDataParser.cs
Normal file
57
iTechSharp/srcbc/cms/CMSCompressedDataParser.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
124
iTechSharp/srcbc/cms/CMSCompressedDataStreamGenerator.cs
Normal file
124
iTechSharp/srcbc/cms/CMSCompressedDataStreamGenerator.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
iTechSharp/srcbc/cms/CMSContentInfoParser.cs
Normal file
47
iTechSharp/srcbc/cms/CMSContentInfoParser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
139
iTechSharp/srcbc/cms/CMSEnvelopedData.cs
Normal file
139
iTechSharp/srcbc/cms/CMSEnvelopedData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
174
iTechSharp/srcbc/cms/CMSEnvelopedDataGenerator.cs
Normal file
174
iTechSharp/srcbc/cms/CMSEnvelopedDataGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
189
iTechSharp/srcbc/cms/CMSEnvelopedDataParser.cs
Normal file
189
iTechSharp/srcbc/cms/CMSEnvelopedDataParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
273
iTechSharp/srcbc/cms/CMSEnvelopedDataStreamGenerator.cs
Normal file
273
iTechSharp/srcbc/cms/CMSEnvelopedDataStreamGenerator.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
551
iTechSharp/srcbc/cms/CMSEnvelopedGenerator.cs
Normal file
551
iTechSharp/srcbc/cms/CMSEnvelopedGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
99
iTechSharp/srcbc/cms/CMSEnvelopedHelper.cs
Normal file
99
iTechSharp/srcbc/cms/CMSEnvelopedHelper.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
25
iTechSharp/srcbc/cms/CMSException.cs
Normal file
25
iTechSharp/srcbc/cms/CMSException.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
68
iTechSharp/srcbc/cms/CMSPBEKey.cs
Normal file
68
iTechSharp/srcbc/cms/CMSPBEKey.cs
Normal 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);
|
||||
}
|
||||
}
|
27
iTechSharp/srcbc/cms/CMSProcessable.cs
Normal file
27
iTechSharp/srcbc/cms/CMSProcessable.cs
Normal 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();
|
||||
}
|
||||
}
|
36
iTechSharp/srcbc/cms/CMSProcessableByteArray.cs
Normal file
36
iTechSharp/srcbc/cms/CMSProcessableByteArray.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
53
iTechSharp/srcbc/cms/CMSProcessableFile.cs
Normal file
53
iTechSharp/srcbc/cms/CMSProcessableFile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
430
iTechSharp/srcbc/cms/CMSSignedData.cs
Normal file
430
iTechSharp/srcbc/cms/CMSSignedData.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
501
iTechSharp/srcbc/cms/CMSSignedDataGenerator.cs
Normal file
501
iTechSharp/srcbc/cms/CMSSignedDataGenerator.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
565
iTechSharp/srcbc/cms/CMSSignedDataParser.cs
Normal file
565
iTechSharp/srcbc/cms/CMSSignedDataParser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
690
iTechSharp/srcbc/cms/CMSSignedDataStreamGenerator.cs
Normal file
690
iTechSharp/srcbc/cms/CMSSignedDataStreamGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
240
iTechSharp/srcbc/cms/CMSSignedGenerator.cs
Normal file
240
iTechSharp/srcbc/cms/CMSSignedGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
332
iTechSharp/srcbc/cms/CMSSignedHelper.cs
Normal file
332
iTechSharp/srcbc/cms/CMSSignedHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
88
iTechSharp/srcbc/cms/CMSTypedStream.cs
Normal file
88
iTechSharp/srcbc/cms/CMSTypedStream.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
161
iTechSharp/srcbc/cms/CMSUtils.cs
Normal file
161
iTechSharp/srcbc/cms/CMSUtils.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
29
iTechSharp/srcbc/cms/CounterSignatureDigestCalculator.cs
Normal file
29
iTechSharp/srcbc/cms/CounterSignatureDigestCalculator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
104
iTechSharp/srcbc/cms/DefaultSignedAttributeTableGenerator.cs
Normal file
104
iTechSharp/srcbc/cms/DefaultSignedAttributeTableGenerator.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
9
iTechSharp/srcbc/cms/IDigestCalculator.cs
Normal file
9
iTechSharp/srcbc/cms/IDigestCalculator.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Cms
|
||||
{
|
||||
internal interface IDigestCalculator
|
||||
{
|
||||
byte[] GetDigest();
|
||||
}
|
||||
}
|
65
iTechSharp/srcbc/cms/KEKRecipientInformation.cs
Normal file
65
iTechSharp/srcbc/cms/KEKRecipientInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
122
iTechSharp/srcbc/cms/KeyAgreeRecipientInformation.cs
Normal file
122
iTechSharp/srcbc/cms/KeyAgreeRecipientInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
111
iTechSharp/srcbc/cms/KeyTransRecipientInformation.cs
Normal file
111
iTechSharp/srcbc/cms/KeyTransRecipientInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
iTechSharp/srcbc/cms/PKCS5Scheme2PBEKey.cs
Normal file
35
iTechSharp/srcbc/cms/PKCS5Scheme2PBEKey.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
74
iTechSharp/srcbc/cms/PasswordRecipientInformation.cs
Normal file
74
iTechSharp/srcbc/cms/PasswordRecipientInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
iTechSharp/srcbc/cms/RecipientId.cs
Normal file
58
iTechSharp/srcbc/cms/RecipientId.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
137
iTechSharp/srcbc/cms/RecipientInformation.cs
Normal file
137
iTechSharp/srcbc/cms/RecipientInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
79
iTechSharp/srcbc/cms/RecipientInformationStore.cs
Normal file
79
iTechSharp/srcbc/cms/RecipientInformationStore.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
51
iTechSharp/srcbc/cms/SignerId.cs
Normal file
51
iTechSharp/srcbc/cms/SignerId.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
631
iTechSharp/srcbc/cms/SignerInformation.cs
Normal file
631
iTechSharp/srcbc/cms/SignerInformation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
72
iTechSharp/srcbc/cms/SignerInformationStore.cs
Normal file
72
iTechSharp/srcbc/cms/SignerInformationStore.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
28
iTechSharp/srcbc/cms/SimpleAttributeTableGenerator.cs
Normal file
28
iTechSharp/srcbc/cms/SimpleAttributeTableGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user