523 lines
18 KiB
C#
523 lines
18 KiB
C#
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.Crypto.Parameters;
|
|
using Org.BouncyCastle.Security;
|
|
using Org.BouncyCastle.Utilities;
|
|
using Org.BouncyCastle.Utilities.Collections;
|
|
using Org.BouncyCastle.Utilities.IO;
|
|
using Org.BouncyCastle.X509;
|
|
|
|
namespace Org.BouncyCastle.Cms;
|
|
|
|
public class CmsSignedDataStreamGenerator : CmsSignedGenerator
|
|
{
|
|
private class DigestAndSignerInfoGeneratorHolder
|
|
{
|
|
internal readonly ISignerInfoGenerator signerInf;
|
|
|
|
internal readonly string digestOID;
|
|
|
|
internal AlgorithmIdentifier DigestAlgorithm => new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), DerNull.Instance);
|
|
|
|
internal DigestAndSignerInfoGeneratorHolder(ISignerInfoGenerator signerInf, string digestOID)
|
|
{
|
|
this.signerInf = signerInf;
|
|
this.digestOID = digestOID;
|
|
}
|
|
}
|
|
|
|
private class SignerInfoGeneratorImpl : ISignerInfoGenerator
|
|
{
|
|
private readonly CmsSignedDataStreamGenerator outer;
|
|
|
|
private readonly SignerIdentifier _signerIdentifier;
|
|
|
|
private readonly string _digestOID;
|
|
|
|
private readonly string _encOID;
|
|
|
|
private readonly CmsAttributeTableGenerator _sAttr;
|
|
|
|
private readonly CmsAttributeTableGenerator _unsAttr;
|
|
|
|
private readonly string _encName;
|
|
|
|
private readonly ISigner _sig;
|
|
|
|
internal SignerInfoGeneratorImpl(CmsSignedDataStreamGenerator outer, AsymmetricKeyParameter key, SignerIdentifier signerIdentifier, string digestOID, string encOID, CmsAttributeTableGenerator sAttr, CmsAttributeTableGenerator unsAttr)
|
|
{
|
|
this.outer = outer;
|
|
_signerIdentifier = signerIdentifier;
|
|
_digestOID = digestOID;
|
|
_encOID = encOID;
|
|
_sAttr = sAttr;
|
|
_unsAttr = unsAttr;
|
|
_encName = Helper.GetEncryptionAlgName(_encOID);
|
|
string digestAlgName = Helper.GetDigestAlgName(_digestOID);
|
|
string algorithm = digestAlgName + "with" + _encName;
|
|
if (_sAttr != null)
|
|
{
|
|
_sig = Helper.GetSignatureInstance(algorithm);
|
|
}
|
|
else if (_encName.Equals("RSA"))
|
|
{
|
|
_sig = Helper.GetSignatureInstance("RSA");
|
|
}
|
|
else
|
|
{
|
|
if (!_encName.Equals("DSA"))
|
|
{
|
|
throw new SignatureException("algorithm: " + _encName + " not supported in base signatures.");
|
|
}
|
|
_sig = Helper.GetSignatureInstance("NONEwithDSA");
|
|
}
|
|
_sig.Init(forSigning: true, new ParametersWithRandom(key, outer.rand));
|
|
}
|
|
|
|
public SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, byte[] calculatedDigest)
|
|
{
|
|
try
|
|
{
|
|
string digestAlgName = Helper.GetDigestAlgName(_digestOID);
|
|
string algorithm = digestAlgName + "with" + _encName;
|
|
byte[] array = calculatedDigest;
|
|
Asn1Set asn1Set = null;
|
|
if (_sAttr != null)
|
|
{
|
|
IDictionary baseParameters = outer.GetBaseParameters(contentType, digestAlgorithm, calculatedDigest);
|
|
Org.BouncyCastle.Asn1.Cms.AttributeTable attributeTable = _sAttr.GetAttributes(baseParameters);
|
|
if (contentType == null && attributeTable != null && attributeTable[CmsAttributes.ContentType] != null)
|
|
{
|
|
IDictionary dictionary = attributeTable.ToDictionary();
|
|
dictionary.Remove(CmsAttributes.ContentType);
|
|
attributeTable = new Org.BouncyCastle.Asn1.Cms.AttributeTable(dictionary);
|
|
}
|
|
asn1Set = outer.GetAttributeSet(attributeTable);
|
|
array = asn1Set.GetEncoded("DER");
|
|
}
|
|
else if (_encName.Equals("RSA"))
|
|
{
|
|
DigestInfo digestInfo = new DigestInfo(digestAlgorithm, calculatedDigest);
|
|
array = digestInfo.GetEncoded("DER");
|
|
}
|
|
_sig.BlockUpdate(array, 0, array.Length);
|
|
byte[] array2 = _sig.GenerateSignature();
|
|
Asn1Set unauthenticatedAttributes = null;
|
|
if (_unsAttr != null)
|
|
{
|
|
IDictionary baseParameters2 = outer.GetBaseParameters(contentType, digestAlgorithm, calculatedDigest);
|
|
baseParameters2[CmsAttributeTableParameter.Signature] = array2.Clone();
|
|
Org.BouncyCastle.Asn1.Cms.AttributeTable attributes = _unsAttr.GetAttributes(baseParameters2);
|
|
unauthenticatedAttributes = outer.GetAttributeSet(attributes);
|
|
}
|
|
Asn1Encodable defaultX509Parameters = SignerUtilities.GetDefaultX509Parameters(algorithm);
|
|
AlgorithmIdentifier encAlgorithmIdentifier = Helper.GetEncAlgorithmIdentifier(new DerObjectIdentifier(_encOID), defaultX509Parameters);
|
|
return new SignerInfo(_signerIdentifier, digestAlgorithm, asn1Set, encAlgorithmIdentifier, new DerOctetString(array2), unauthenticatedAttributes);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new CmsStreamException("encoding error.", e);
|
|
}
|
|
catch (SignatureException e2)
|
|
{
|
|
throw new CmsStreamException("error creating signature.", e2);
|
|
}
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
DoClose();
|
|
base.Close();
|
|
}
|
|
|
|
private void DoClose()
|
|
{
|
|
Platform.Dispose(_out);
|
|
_eiGen.Close();
|
|
outer._digests.Clear();
|
|
if (outer._certs.Count > 0)
|
|
{
|
|
Asn1Set obj = (outer.UseDerForCerts ? CmsUtilities.CreateDerSetFromList(outer._certs) : CmsUtilities.CreateBerSetFromList(outer._certs));
|
|
WriteToGenerator(_sigGen, new BerTaggedObject(explicitly: false, 0, obj));
|
|
}
|
|
if (outer._crls.Count > 0)
|
|
{
|
|
Asn1Set obj2 = (outer.UseDerForCrls ? CmsUtilities.CreateDerSetFromList(outer._crls) : CmsUtilities.CreateBerSetFromList(outer._crls));
|
|
WriteToGenerator(_sigGen, new BerTaggedObject(explicitly: false, 1, obj2));
|
|
}
|
|
foreach (object messageDigest in outer._messageDigests)
|
|
{
|
|
DictionaryEntry dictionaryEntry = (DictionaryEntry)messageDigest;
|
|
outer._messageHashes.Add(dictionaryEntry.Key, DigestUtilities.DoFinal((IDigest)dictionaryEntry.Value));
|
|
}
|
|
Asn1EncodableVector asn1EncodableVector = new Asn1EncodableVector();
|
|
foreach (DigestAndSignerInfoGeneratorHolder signerInf in outer._signerInfs)
|
|
{
|
|
AlgorithmIdentifier digestAlgorithm = signerInf.DigestAlgorithm;
|
|
byte[] array = (byte[])outer._messageHashes[Helper.GetDigestAlgName(signerInf.digestOID)];
|
|
outer._digests[signerInf.digestOID] = array.Clone();
|
|
asn1EncodableVector.Add(signerInf.signerInf.Generate(_contentOID, digestAlgorithm, array));
|
|
}
|
|
foreach (SignerInformation signer in outer._signers)
|
|
{
|
|
asn1EncodableVector.Add(signer.ToSignerInfo());
|
|
}
|
|
WriteToGenerator(_sigGen, new DerSet(asn1EncodableVector));
|
|
_sigGen.Close();
|
|
_sGen.Close();
|
|
}
|
|
|
|
private static void WriteToGenerator(Asn1Generator ag, Asn1Encodable ae)
|
|
{
|
|
byte[] encoded = ae.GetEncoded();
|
|
ag.GetRawOutputStream().Write(encoded, 0, encoded.Length);
|
|
}
|
|
}
|
|
|
|
private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
|
|
|
|
private readonly IList _signerInfs = Platform.CreateArrayList();
|
|
|
|
private readonly ISet _messageDigestOids = new HashSet();
|
|
|
|
private readonly IDictionary _messageDigests = Platform.CreateHashtable();
|
|
|
|
private readonly IDictionary _messageHashes = Platform.CreateHashtable();
|
|
|
|
private bool _messageDigestsLocked;
|
|
|
|
private int _bufferSize;
|
|
|
|
public CmsSignedDataStreamGenerator()
|
|
{
|
|
}
|
|
|
|
public CmsSignedDataStreamGenerator(SecureRandom rand)
|
|
: base(rand)
|
|
{
|
|
}
|
|
|
|
public void SetBufferSize(int bufferSize)
|
|
{
|
|
_bufferSize = bufferSize;
|
|
}
|
|
|
|
public void AddDigests(params string[] digestOids)
|
|
{
|
|
AddDigests((IEnumerable)digestOids);
|
|
}
|
|
|
|
public void AddDigests(IEnumerable digestOids)
|
|
{
|
|
foreach (string digestOid in digestOids)
|
|
{
|
|
ConfigureDigest(digestOid);
|
|
}
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string digestOid)
|
|
{
|
|
AddSigner(privateKey, cert, digestOid, new DefaultSignedAttributeTableGenerator(), null);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string encryptionOid, string digestOid)
|
|
{
|
|
AddSigner(privateKey, cert, encryptionOid, digestOid, new DefaultSignedAttributeTableGenerator(), null);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string digestOid, Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttr, Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttr)
|
|
{
|
|
AddSigner(privateKey, cert, digestOid, new DefaultSignedAttributeTableGenerator(signedAttr), new SimpleAttributeTableGenerator(unsignedAttr));
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string encryptionOid, string digestOid, Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttr, Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttr)
|
|
{
|
|
AddSigner(privateKey, cert, encryptionOid, digestOid, new DefaultSignedAttributeTableGenerator(signedAttr), new SimpleAttributeTableGenerator(unsignedAttr));
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string digestOid, CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator)
|
|
{
|
|
AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOid), digestOid, signedAttrGenerator, unsignedAttrGenerator);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, X509Certificate cert, string encryptionOid, string digestOid, CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator)
|
|
{
|
|
DoAddSigner(privateKey, CmsSignedGenerator.GetSignerIdentifier(cert), encryptionOid, digestOid, signedAttrGenerator, unsignedAttrGenerator);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, byte[] subjectKeyID, string digestOid)
|
|
{
|
|
AddSigner(privateKey, subjectKeyID, digestOid, new DefaultSignedAttributeTableGenerator(), null);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, byte[] subjectKeyID, string encryptionOid, string digestOid)
|
|
{
|
|
AddSigner(privateKey, subjectKeyID, encryptionOid, digestOid, new DefaultSignedAttributeTableGenerator(), null);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, byte[] subjectKeyID, string digestOid, Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttr, Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttr)
|
|
{
|
|
AddSigner(privateKey, subjectKeyID, digestOid, new DefaultSignedAttributeTableGenerator(signedAttr), new SimpleAttributeTableGenerator(unsignedAttr));
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, byte[] subjectKeyID, string digestOid, CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator)
|
|
{
|
|
AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOid), digestOid, signedAttrGenerator, unsignedAttrGenerator);
|
|
}
|
|
|
|
public void AddSigner(AsymmetricKeyParameter privateKey, byte[] subjectKeyID, string encryptionOid, string digestOid, CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator)
|
|
{
|
|
DoAddSigner(privateKey, CmsSignedGenerator.GetSignerIdentifier(subjectKeyID), encryptionOid, digestOid, signedAttrGenerator, unsignedAttrGenerator);
|
|
}
|
|
|
|
private void DoAddSigner(AsymmetricKeyParameter privateKey, SignerIdentifier signerIdentifier, string encryptionOid, string digestOid, CmsAttributeTableGenerator signedAttrGenerator, CmsAttributeTableGenerator unsignedAttrGenerator)
|
|
{
|
|
ConfigureDigest(digestOid);
|
|
SignerInfoGeneratorImpl signerInf = new SignerInfoGeneratorImpl(this, privateKey, signerIdentifier, digestOid, encryptionOid, signedAttrGenerator, unsignedAttrGenerator);
|
|
_signerInfs.Add(new DigestAndSignerInfoGeneratorHolder(signerInf, digestOid));
|
|
}
|
|
|
|
internal override void AddSignerCallback(SignerInformation si)
|
|
{
|
|
RegisterDigestOid(si.DigestAlgorithmID.Algorithm.Id);
|
|
}
|
|
|
|
public Stream Open(Stream outStream)
|
|
{
|
|
return Open(outStream, encapsulate: false);
|
|
}
|
|
|
|
public Stream Open(Stream outStream, bool encapsulate)
|
|
{
|
|
return Open(outStream, CmsSignedGenerator.Data, encapsulate);
|
|
}
|
|
|
|
public Stream Open(Stream outStream, bool encapsulate, Stream dataOutputStream)
|
|
{
|
|
return Open(outStream, CmsSignedGenerator.Data, encapsulate, dataOutputStream);
|
|
}
|
|
|
|
public Stream Open(Stream outStream, string signedContentType, bool encapsulate)
|
|
{
|
|
return Open(outStream, signedContentType, encapsulate, null);
|
|
}
|
|
|
|
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");
|
|
}
|
|
_messageDigestsLocked = true;
|
|
BerSequenceGenerator berSequenceGenerator = new BerSequenceGenerator(outStream);
|
|
berSequenceGenerator.AddObject(CmsObjectIdentifiers.SignedData);
|
|
BerSequenceGenerator berSequenceGenerator2 = new BerSequenceGenerator(berSequenceGenerator.GetRawOutputStream(), 0, isExplicit: true);
|
|
DerObjectIdentifier derObjectIdentifier = ((signedContentType == null) ? null : new DerObjectIdentifier(signedContentType));
|
|
berSequenceGenerator2.AddObject(CalculateVersion(derObjectIdentifier));
|
|
Asn1EncodableVector asn1EncodableVector = new Asn1EncodableVector();
|
|
foreach (string messageDigestOid in _messageDigestOids)
|
|
{
|
|
asn1EncodableVector.Add(new AlgorithmIdentifier(new DerObjectIdentifier(messageDigestOid), DerNull.Instance));
|
|
}
|
|
byte[] encoded = new DerSet(asn1EncodableVector).GetEncoded();
|
|
berSequenceGenerator2.GetRawOutputStream().Write(encoded, 0, encoded.Length);
|
|
BerSequenceGenerator berSequenceGenerator3 = new BerSequenceGenerator(berSequenceGenerator2.GetRawOutputStream());
|
|
berSequenceGenerator3.AddObject(derObjectIdentifier);
|
|
Stream s = (encapsulate ? CmsUtilities.CreateBerOctetOutputStream(berSequenceGenerator3.GetRawOutputStream(), 0, isExplicit: true, _bufferSize) : null);
|
|
Stream safeTeeOutputStream = GetSafeTeeOutputStream(dataOutputStream, s);
|
|
Stream outStream2 = AttachDigestsToOutputStream(_messageDigests.Values, safeTeeOutputStream);
|
|
return new CmsSignedDataOutputStream(this, outStream2, signedContentType, berSequenceGenerator, berSequenceGenerator2, berSequenceGenerator3);
|
|
}
|
|
|
|
private void RegisterDigestOid(string digestOid)
|
|
{
|
|
if (_messageDigestsLocked)
|
|
{
|
|
if (!_messageDigestOids.Contains(digestOid))
|
|
{
|
|
throw new InvalidOperationException("Cannot register new digest OIDs after the data stream is opened");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_messageDigestOids.Add(digestOid);
|
|
}
|
|
}
|
|
|
|
private void ConfigureDigest(string digestOid)
|
|
{
|
|
RegisterDigestOid(digestOid);
|
|
string digestAlgName = Helper.GetDigestAlgName(digestOid);
|
|
IDigest digest = (IDigest)_messageDigests[digestAlgName];
|
|
if (digest == null)
|
|
{
|
|
if (_messageDigestsLocked)
|
|
{
|
|
throw new InvalidOperationException("Cannot configure new digests after the data stream is opened");
|
|
}
|
|
digest = Helper.GetDigestInstance(digestAlgName);
|
|
_messageDigests[digestAlgName] = digest;
|
|
}
|
|
}
|
|
|
|
internal void Generate(Stream outStream, string eContentType, bool encapsulate, Stream dataOutputStream, CmsProcessable content)
|
|
{
|
|
Stream stream = Open(outStream, eContentType, encapsulate, dataOutputStream);
|
|
content?.Write(stream);
|
|
Platform.Dispose(stream);
|
|
}
|
|
|
|
private DerInteger CalculateVersion(DerObjectIdentifier contentOid)
|
|
{
|
|
bool flag = false;
|
|
bool flag2 = false;
|
|
bool flag3 = false;
|
|
bool flag4 = false;
|
|
if (_certs != null)
|
|
{
|
|
foreach (object cert in _certs)
|
|
{
|
|
if (cert is Asn1TaggedObject)
|
|
{
|
|
Asn1TaggedObject asn1TaggedObject = (Asn1TaggedObject)cert;
|
|
if (asn1TaggedObject.TagNo == 1)
|
|
{
|
|
flag3 = true;
|
|
}
|
|
else if (asn1TaggedObject.TagNo == 2)
|
|
{
|
|
flag4 = true;
|
|
}
|
|
else if (asn1TaggedObject.TagNo == 3)
|
|
{
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (flag)
|
|
{
|
|
return new DerInteger(5);
|
|
}
|
|
if (_crls != null)
|
|
{
|
|
foreach (object crl in _crls)
|
|
{
|
|
if (crl is Asn1TaggedObject)
|
|
{
|
|
flag2 = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (flag2)
|
|
{
|
|
return new DerInteger(5);
|
|
}
|
|
if (flag4)
|
|
{
|
|
return new DerInteger(4);
|
|
}
|
|
if (flag3 || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(_signers))
|
|
{
|
|
return new DerInteger(3);
|
|
}
|
|
return new DerInteger(1);
|
|
}
|
|
|
|
private bool CheckForVersion3(IList signerInfos)
|
|
{
|
|
foreach (SignerInformation signerInfo in signerInfos)
|
|
{
|
|
SignerInfo instance = SignerInfo.GetInstance(signerInfo.ToSignerInfo());
|
|
if (instance.Version.Value.IntValue == 3)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static Stream AttachDigestsToOutputStream(ICollection digests, Stream s)
|
|
{
|
|
Stream stream = s;
|
|
foreach (IDigest digest in digests)
|
|
{
|
|
stream = GetSafeTeeOutputStream(stream, new DigestSink(digest));
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
private static Stream GetSafeOutputStream(Stream s)
|
|
{
|
|
if (s == null)
|
|
{
|
|
return new NullOutputStream();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private static Stream GetSafeTeeOutputStream(Stream s1, Stream s2)
|
|
{
|
|
if (s1 == null)
|
|
{
|
|
return GetSafeOutputStream(s2);
|
|
}
|
|
if (s2 == null)
|
|
{
|
|
return GetSafeOutputStream(s1);
|
|
}
|
|
return new TeeOutputStream(s1, s2);
|
|
}
|
|
}
|