You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
12 KiB
444 lines
12 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.Collections; |
|
using System.IO; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cms; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security.Certificates; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509.Store; |
|
|
|
namespace BestHTTP.SecureProtocol.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.IntValueExact; } |
|
} |
|
|
|
internal IX509Store GetCertificates() |
|
{ |
|
return Helper.GetCertificates(signedData.Certificates); |
|
} |
|
|
|
/** |
|
* return the collection of signers that are associated with the |
|
* signatures for the message. |
|
*/ |
|
public SignerInformationStore GetSignerInfos() |
|
{ |
|
if (signerInfoStore == null) |
|
{ |
|
IList signerInfos = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(); |
|
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.Algorithm.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; |
|
} |
|
|
|
|
|
public string SignedContentTypeOid |
|
{ |
|
get { return signedData.EncapContentInfo.ContentType.Id; } |
|
} |
|
|
|
/// <summary> |
|
/// Return the <c>DerObjectIdentifier</c> associated with the encapsulated |
|
/// content info structure carried in the signed data. |
|
/// </summary> |
|
public DerObjectIdentifier SignedContentType |
|
{ |
|
get { return signedData.EncapContentInfo.ContentType; } |
|
} |
|
|
|
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(); |
|
} |
|
|
|
/** |
|
* return the ASN.1 encoded representation of this object using the specified encoding. |
|
* |
|
* @param encoding the ASN.1 encoding format to use ("BER" or "DER"). |
|
*/ |
|
public byte[] GetEncoded(string encoding) |
|
{ |
|
return contentInfo.GetEncoded(encoding); |
|
} |
|
|
|
/** |
|
* 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(Helper.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 BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.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; |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|