1019 lines
27 KiB
C#
1019 lines
27 KiB
C#
|
//
|
||
|
// PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard
|
||
|
// http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html
|
||
|
//
|
||
|
// Authors:
|
||
|
// Sebastien Pouliot <sebastien@ximian.com>
|
||
|
// Daniel Granath <dgranath#gmail.com>
|
||
|
//
|
||
|
// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
|
||
|
// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||
|
// a copy of this software and associated documentation files (the
|
||
|
// "Software"), to deal in the Software without restriction, including
|
||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||
|
// the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be
|
||
|
// included in all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Security.Cryptography;
|
||
|
|
||
|
using Mono.Security.X509;
|
||
|
|
||
|
namespace Mono.Security {
|
||
|
|
||
|
#if INSIDE_CORLIB
|
||
|
internal
|
||
|
#else
|
||
|
public
|
||
|
#endif
|
||
|
sealed class PKCS7 {
|
||
|
|
||
|
public class Oid {
|
||
|
// pkcs 1
|
||
|
public const string rsaEncryption = "1.2.840.113549.1.1.1";
|
||
|
// pkcs 7
|
||
|
public const string data = "1.2.840.113549.1.7.1";
|
||
|
public const string signedData = "1.2.840.113549.1.7.2";
|
||
|
public const string envelopedData = "1.2.840.113549.1.7.3";
|
||
|
public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4";
|
||
|
public const string digestedData = "1.2.840.113549.1.7.5";
|
||
|
public const string encryptedData = "1.2.840.113549.1.7.6";
|
||
|
// pkcs 9
|
||
|
public const string contentType = "1.2.840.113549.1.9.3";
|
||
|
public const string messageDigest = "1.2.840.113549.1.9.4";
|
||
|
public const string signingTime = "1.2.840.113549.1.9.5";
|
||
|
public const string countersignature = "1.2.840.113549.1.9.6";
|
||
|
|
||
|
public Oid ()
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private PKCS7 ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static public ASN1 Attribute (string oid, ASN1 value)
|
||
|
{
|
||
|
ASN1 attr = new ASN1 (0x30);
|
||
|
attr.Add (ASN1Convert.FromOid (oid));
|
||
|
ASN1 aset = attr.Add (new ASN1 (0x31));
|
||
|
aset.Add (value);
|
||
|
return attr;
|
||
|
}
|
||
|
|
||
|
static public ASN1 AlgorithmIdentifier (string oid)
|
||
|
{
|
||
|
ASN1 ai = new ASN1 (0x30);
|
||
|
ai.Add (ASN1Convert.FromOid (oid));
|
||
|
ai.Add (new ASN1 (0x05)); // NULL
|
||
|
return ai;
|
||
|
}
|
||
|
|
||
|
static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters)
|
||
|
{
|
||
|
ASN1 ai = new ASN1 (0x30);
|
||
|
ai.Add (ASN1Convert.FromOid (oid));
|
||
|
ai.Add (parameters);
|
||
|
return ai;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* IssuerAndSerialNumber ::= SEQUENCE {
|
||
|
* issuer Name,
|
||
|
* serialNumber CertificateSerialNumber
|
||
|
* }
|
||
|
*/
|
||
|
static public ASN1 IssuerAndSerialNumber (X509Certificate x509)
|
||
|
{
|
||
|
ASN1 issuer = null;
|
||
|
ASN1 serial = null;
|
||
|
ASN1 cert = new ASN1 (x509.RawData);
|
||
|
int tbs = 0;
|
||
|
bool flag = false;
|
||
|
while (tbs < cert[0].Count) {
|
||
|
ASN1 e = cert[0][tbs++];
|
||
|
if (e.Tag == 0x02)
|
||
|
serial = e;
|
||
|
else if (e.Tag == 0x30) {
|
||
|
if (flag) {
|
||
|
issuer = e;
|
||
|
break;
|
||
|
}
|
||
|
flag = true;
|
||
|
}
|
||
|
}
|
||
|
ASN1 iasn = new ASN1 (0x30);
|
||
|
iasn.Add (issuer);
|
||
|
iasn.Add (serial);
|
||
|
return iasn;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ContentInfo ::= SEQUENCE {
|
||
|
* contentType ContentType,
|
||
|
* content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
|
||
|
* }
|
||
|
* ContentType ::= OBJECT IDENTIFIER
|
||
|
*/
|
||
|
public class ContentInfo {
|
||
|
|
||
|
private string contentType;
|
||
|
private ASN1 content;
|
||
|
|
||
|
public ContentInfo ()
|
||
|
{
|
||
|
content = new ASN1 (0xA0);
|
||
|
}
|
||
|
|
||
|
public ContentInfo (string oid) : this ()
|
||
|
{
|
||
|
contentType = oid;
|
||
|
}
|
||
|
|
||
|
public ContentInfo (byte[] data)
|
||
|
: this (new ASN1 (data)) {}
|
||
|
|
||
|
public ContentInfo (ASN1 asn1)
|
||
|
{
|
||
|
// SEQUENCE with 1 or 2 elements
|
||
|
if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2)))
|
||
|
throw new ArgumentException ("Invalid ASN1");
|
||
|
if (asn1[0].Tag != 0x06)
|
||
|
throw new ArgumentException ("Invalid contentType");
|
||
|
contentType = ASN1Convert.ToOid (asn1[0]);
|
||
|
if (asn1.Count > 1) {
|
||
|
if (asn1[1].Tag != 0xA0)
|
||
|
throw new ArgumentException ("Invalid content");
|
||
|
content = asn1[1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ASN1 ASN1 {
|
||
|
get { return GetASN1(); }
|
||
|
}
|
||
|
|
||
|
public ASN1 Content {
|
||
|
get { return content; }
|
||
|
set { content = value; }
|
||
|
}
|
||
|
|
||
|
public string ContentType {
|
||
|
get { return contentType; }
|
||
|
set { contentType = value; }
|
||
|
}
|
||
|
|
||
|
internal ASN1 GetASN1 ()
|
||
|
{
|
||
|
// ContentInfo ::= SEQUENCE {
|
||
|
ASN1 contentInfo = new ASN1 (0x30);
|
||
|
// contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER
|
||
|
contentInfo.Add (ASN1Convert.FromOid (contentType));
|
||
|
// content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
|
||
|
if ((content != null) && (content.Count > 0))
|
||
|
contentInfo.Add (content);
|
||
|
return contentInfo;
|
||
|
}
|
||
|
|
||
|
public byte[] GetBytes ()
|
||
|
{
|
||
|
return GetASN1 ().GetBytes ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* EncryptedData ::= SEQUENCE {
|
||
|
* version INTEGER {edVer0(0)} (edVer0),
|
||
|
* encryptedContentInfo EncryptedContentInfo
|
||
|
* }
|
||
|
*/
|
||
|
public class EncryptedData {
|
||
|
private byte _version;
|
||
|
private ContentInfo _content;
|
||
|
private ContentInfo _encryptionAlgorithm;
|
||
|
private byte[] _encrypted;
|
||
|
|
||
|
public EncryptedData ()
|
||
|
{
|
||
|
_version = 0;
|
||
|
}
|
||
|
|
||
|
public EncryptedData (byte[] data)
|
||
|
: this (new ASN1 (data))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public EncryptedData (ASN1 asn1) : this ()
|
||
|
{
|
||
|
if ((asn1.Tag != 0x30) || (asn1.Count < 2))
|
||
|
throw new ArgumentException ("Invalid EncryptedData");
|
||
|
|
||
|
if (asn1 [0].Tag != 0x02)
|
||
|
throw new ArgumentException ("Invalid version");
|
||
|
_version = asn1 [0].Value [0];
|
||
|
|
||
|
ASN1 encryptedContentInfo = asn1 [1];
|
||
|
if (encryptedContentInfo.Tag != 0x30)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo");
|
||
|
|
||
|
ASN1 contentType = encryptedContentInfo [0];
|
||
|
if (contentType.Tag != 0x06)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
|
||
|
_content = new ContentInfo (ASN1Convert.ToOid (contentType));
|
||
|
|
||
|
ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
|
||
|
if (contentEncryptionAlgorithm.Tag != 0x30)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
|
||
|
_encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0]));
|
||
|
_encryptionAlgorithm.Content = contentEncryptionAlgorithm [1];
|
||
|
|
||
|
ASN1 encryptedContent = encryptedContentInfo [2];
|
||
|
if (encryptedContent.Tag != 0x80)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent");
|
||
|
_encrypted = encryptedContent.Value;
|
||
|
}
|
||
|
|
||
|
public ASN1 ASN1 {
|
||
|
get { return GetASN1(); }
|
||
|
}
|
||
|
|
||
|
public ContentInfo ContentInfo {
|
||
|
get { return _content; }
|
||
|
}
|
||
|
|
||
|
public ContentInfo EncryptionAlgorithm {
|
||
|
get { return _encryptionAlgorithm; }
|
||
|
}
|
||
|
|
||
|
public byte[] EncryptedContent {
|
||
|
get {
|
||
|
if (_encrypted == null)
|
||
|
return null;
|
||
|
return (byte[]) _encrypted.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public byte Version {
|
||
|
get { return _version; }
|
||
|
set { _version = value; }
|
||
|
}
|
||
|
|
||
|
// methods
|
||
|
|
||
|
internal ASN1 GetASN1 ()
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public byte[] GetBytes ()
|
||
|
{
|
||
|
return GetASN1 ().GetBytes ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* EnvelopedData ::= SEQUENCE {
|
||
|
* version Version,
|
||
|
* recipientInfos RecipientInfos,
|
||
|
* encryptedContentInfo EncryptedContentInfo
|
||
|
* }
|
||
|
*
|
||
|
* RecipientInfos ::= SET OF RecipientInfo
|
||
|
*
|
||
|
* EncryptedContentInfo ::= SEQUENCE {
|
||
|
* contentType ContentType,
|
||
|
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
|
||
|
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
|
||
|
* }
|
||
|
*
|
||
|
* EncryptedContent ::= OCTET STRING
|
||
|
*
|
||
|
*/
|
||
|
public class EnvelopedData {
|
||
|
private byte _version;
|
||
|
private ContentInfo _content;
|
||
|
private ContentInfo _encryptionAlgorithm;
|
||
|
private ArrayList _recipientInfos;
|
||
|
private byte[] _encrypted;
|
||
|
|
||
|
public EnvelopedData ()
|
||
|
{
|
||
|
_version = 0;
|
||
|
_content = new ContentInfo ();
|
||
|
_encryptionAlgorithm = new ContentInfo ();
|
||
|
_recipientInfos = new ArrayList ();
|
||
|
}
|
||
|
|
||
|
public EnvelopedData (byte[] data)
|
||
|
: this (new ASN1 (data))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public EnvelopedData (ASN1 asn1) : this ()
|
||
|
{
|
||
|
if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3))
|
||
|
throw new ArgumentException ("Invalid EnvelopedData");
|
||
|
|
||
|
if (asn1[0][0].Tag != 0x02)
|
||
|
throw new ArgumentException ("Invalid version");
|
||
|
_version = asn1[0][0].Value[0];
|
||
|
|
||
|
// recipientInfos
|
||
|
|
||
|
ASN1 recipientInfos = asn1 [0][1];
|
||
|
if (recipientInfos.Tag != 0x31)
|
||
|
throw new ArgumentException ("missing RecipientInfos");
|
||
|
for (int i=0; i < recipientInfos.Count; i++) {
|
||
|
ASN1 recipientInfo = recipientInfos [i];
|
||
|
_recipientInfos.Add (new RecipientInfo (recipientInfo));
|
||
|
}
|
||
|
|
||
|
ASN1 encryptedContentInfo = asn1[0][2];
|
||
|
if (encryptedContentInfo.Tag != 0x30)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo");
|
||
|
|
||
|
ASN1 contentType = encryptedContentInfo [0];
|
||
|
if (contentType.Tag != 0x06)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
|
||
|
_content = new ContentInfo (ASN1Convert.ToOid (contentType));
|
||
|
|
||
|
ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
|
||
|
if (contentEncryptionAlgorithm.Tag != 0x30)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
|
||
|
_encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0]));
|
||
|
_encryptionAlgorithm.Content = contentEncryptionAlgorithm [1];
|
||
|
|
||
|
ASN1 encryptedContent = encryptedContentInfo [2];
|
||
|
if (encryptedContent.Tag != 0x80)
|
||
|
throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent");
|
||
|
_encrypted = encryptedContent.Value;
|
||
|
}
|
||
|
|
||
|
public ArrayList RecipientInfos {
|
||
|
get { return _recipientInfos; }
|
||
|
}
|
||
|
|
||
|
public ASN1 ASN1 {
|
||
|
get { return GetASN1(); }
|
||
|
}
|
||
|
|
||
|
public ContentInfo ContentInfo {
|
||
|
get { return _content; }
|
||
|
}
|
||
|
|
||
|
public ContentInfo EncryptionAlgorithm {
|
||
|
get { return _encryptionAlgorithm; }
|
||
|
}
|
||
|
|
||
|
public byte[] EncryptedContent {
|
||
|
get {
|
||
|
if (_encrypted == null)
|
||
|
return null;
|
||
|
return (byte[]) _encrypted.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public byte Version {
|
||
|
get { return _version; }
|
||
|
set { _version = value; }
|
||
|
}
|
||
|
|
||
|
internal ASN1 GetASN1 ()
|
||
|
{
|
||
|
// SignedData ::= SEQUENCE {
|
||
|
ASN1 signedData = new ASN1 (0x30);
|
||
|
// version Version -> Version ::= INTEGER
|
||
|
/* byte[] ver = { _version };
|
||
|
signedData.Add (new ASN1 (0x02, ver));
|
||
|
// digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
|
||
|
ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
|
||
|
if (hashAlgorithm != null) {
|
||
|
string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm);
|
||
|
digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
|
||
|
}
|
||
|
|
||
|
// contentInfo ContentInfo,
|
||
|
ASN1 ci = contentInfo.ASN1;
|
||
|
signedData.Add (ci);
|
||
|
if ((mda == null) && (hashAlgorithm != null)) {
|
||
|
// automatically add the messageDigest authenticated attribute
|
||
|
HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
|
||
|
byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
|
||
|
ASN1 md = new ASN1 (0x30);
|
||
|
mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash)));
|
||
|
signerInfo.AuthenticatedAttributes.Add (mda);
|
||
|
}
|
||
|
|
||
|
// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
|
||
|
if (certs.Count > 0) {
|
||
|
ASN1 a0 = signedData.Add (new ASN1 (0xA0));
|
||
|
foreach (X509Certificate x in certs)
|
||
|
a0.Add (new ASN1 (x.RawData));
|
||
|
}
|
||
|
// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
|
||
|
if (crls.Count > 0) {
|
||
|
ASN1 a1 = signedData.Add (new ASN1 (0xA1));
|
||
|
foreach (byte[] crl in crls)
|
||
|
a1.Add (new ASN1 (crl));
|
||
|
}
|
||
|
// signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
|
||
|
ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
|
||
|
if (signerInfo.Key != null)
|
||
|
signerInfos.Add (signerInfo.ASN1);*/
|
||
|
return signedData;
|
||
|
}
|
||
|
|
||
|
public byte[] GetBytes () {
|
||
|
return GetASN1 ().GetBytes ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* RecipientInfo ::= SEQUENCE {
|
||
|
* version Version,
|
||
|
* issuerAndSerialNumber IssuerAndSerialNumber,
|
||
|
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
|
||
|
* encryptedKey EncryptedKey
|
||
|
* }
|
||
|
*
|
||
|
* KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
||
|
*
|
||
|
* EncryptedKey ::= OCTET STRING
|
||
|
*/
|
||
|
public class RecipientInfo {
|
||
|
|
||
|
private int _version;
|
||
|
private string _oid;
|
||
|
private byte[] _key;
|
||
|
private byte[] _ski;
|
||
|
private string _issuer;
|
||
|
private byte[] _serial;
|
||
|
|
||
|
public RecipientInfo () {}
|
||
|
|
||
|
public RecipientInfo (ASN1 data)
|
||
|
{
|
||
|
if (data.Tag != 0x30)
|
||
|
throw new ArgumentException ("Invalid RecipientInfo");
|
||
|
|
||
|
ASN1 version = data [0];
|
||
|
if (version.Tag != 0x02)
|
||
|
throw new ArgumentException ("missing Version");
|
||
|
_version = version.Value [0];
|
||
|
|
||
|
// issuerAndSerialNumber IssuerAndSerialNumber
|
||
|
ASN1 subjectIdentifierType = data [1];
|
||
|
if ((subjectIdentifierType.Tag == 0x80) && (_version == 3)) {
|
||
|
_ski = subjectIdentifierType.Value;
|
||
|
}
|
||
|
else {
|
||
|
_issuer = X501.ToString (subjectIdentifierType [0]);
|
||
|
_serial = subjectIdentifierType [1].Value;
|
||
|
}
|
||
|
|
||
|
ASN1 keyEncryptionAlgorithm = data [2];
|
||
|
_oid = ASN1Convert.ToOid (keyEncryptionAlgorithm [0]);
|
||
|
|
||
|
ASN1 encryptedKey = data [3];
|
||
|
_key = encryptedKey.Value;
|
||
|
}
|
||
|
|
||
|
public string Oid {
|
||
|
get { return _oid; }
|
||
|
}
|
||
|
|
||
|
public byte[] Key {
|
||
|
get {
|
||
|
if (_key == null)
|
||
|
return null;
|
||
|
return (byte[]) _key.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public byte[] SubjectKeyIdentifier {
|
||
|
get {
|
||
|
if (_ski == null)
|
||
|
return null;
|
||
|
return (byte[]) _ski.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string Issuer {
|
||
|
get { return _issuer; }
|
||
|
}
|
||
|
|
||
|
public byte[] Serial {
|
||
|
get {
|
||
|
if (_serial == null)
|
||
|
return null;
|
||
|
return (byte[]) _serial.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int Version {
|
||
|
get { return _version; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* SignedData ::= SEQUENCE {
|
||
|
* version Version,
|
||
|
* digestAlgorithms DigestAlgorithmIdentifiers,
|
||
|
* contentInfo ContentInfo,
|
||
|
* certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
|
||
|
* crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
|
||
|
* signerInfos SignerInfos
|
||
|
* }
|
||
|
*/
|
||
|
public class SignedData {
|
||
|
private byte version;
|
||
|
private string hashAlgorithm;
|
||
|
private ContentInfo contentInfo;
|
||
|
private X509CertificateCollection certs;
|
||
|
private ArrayList crls;
|
||
|
private SignerInfo signerInfo;
|
||
|
private bool mda;
|
||
|
private bool signed;
|
||
|
|
||
|
public SignedData ()
|
||
|
{
|
||
|
version = 1;
|
||
|
contentInfo = new ContentInfo ();
|
||
|
certs = new X509CertificateCollection ();
|
||
|
crls = new ArrayList ();
|
||
|
signerInfo = new SignerInfo ();
|
||
|
mda = true;
|
||
|
signed = false;
|
||
|
}
|
||
|
|
||
|
public SignedData (byte[] data)
|
||
|
: this (new ASN1 (data))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public SignedData (ASN1 asn1)
|
||
|
{
|
||
|
if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4))
|
||
|
throw new ArgumentException ("Invalid SignedData");
|
||
|
|
||
|
if (asn1[0][0].Tag != 0x02)
|
||
|
throw new ArgumentException ("Invalid version");
|
||
|
version = asn1[0][0].Value[0];
|
||
|
|
||
|
contentInfo = new ContentInfo (asn1[0][2]);
|
||
|
|
||
|
int n = 3;
|
||
|
certs = new X509CertificateCollection ();
|
||
|
if (asn1[0][n].Tag == 0xA0) {
|
||
|
for (int i=0; i < asn1[0][n].Count; i++)
|
||
|
certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ()));
|
||
|
n++;
|
||
|
}
|
||
|
|
||
|
crls = new ArrayList ();
|
||
|
if (asn1[0][n].Tag == 0xA1) {
|
||
|
for (int i=0; i < asn1[0][n].Count; i++)
|
||
|
crls.Add (asn1[0][n][i].GetBytes ());
|
||
|
n++;
|
||
|
}
|
||
|
|
||
|
if (asn1[0][n].Count > 0)
|
||
|
signerInfo = new SignerInfo (asn1[0][n]);
|
||
|
else
|
||
|
signerInfo = new SignerInfo ();
|
||
|
|
||
|
// Exchange hash algorithm Oid from SignerInfo
|
||
|
if (signerInfo.HashName != null) {
|
||
|
HashName = OidToName(signerInfo.HashName);
|
||
|
}
|
||
|
|
||
|
// Check if SignerInfo has authenticated attributes
|
||
|
mda = (signerInfo.AuthenticatedAttributes.Count > 0);
|
||
|
}
|
||
|
|
||
|
public ASN1 ASN1 {
|
||
|
get { return GetASN1(); }
|
||
|
}
|
||
|
|
||
|
public X509CertificateCollection Certificates {
|
||
|
get { return certs; }
|
||
|
}
|
||
|
|
||
|
public ContentInfo ContentInfo {
|
||
|
get { return contentInfo; }
|
||
|
}
|
||
|
|
||
|
public ArrayList Crls {
|
||
|
get { return crls; }
|
||
|
}
|
||
|
|
||
|
public string HashName {
|
||
|
get { return hashAlgorithm; }
|
||
|
// todo add validation
|
||
|
set {
|
||
|
hashAlgorithm = value;
|
||
|
signerInfo.HashName = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SignerInfo SignerInfo {
|
||
|
get { return signerInfo; }
|
||
|
}
|
||
|
|
||
|
public byte Version {
|
||
|
get { return version; }
|
||
|
set { version = value; }
|
||
|
}
|
||
|
|
||
|
public bool UseAuthenticatedAttributes {
|
||
|
get { return mda; }
|
||
|
set { mda = value; }
|
||
|
}
|
||
|
|
||
|
public bool VerifySignature (AsymmetricAlgorithm aa)
|
||
|
{
|
||
|
if (aa == null) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter (aa);
|
||
|
r.SetHashAlgorithm (hashAlgorithm);
|
||
|
HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
|
||
|
|
||
|
byte[] signature = signerInfo.Signature;
|
||
|
byte[] hash = null;
|
||
|
|
||
|
if (mda) {
|
||
|
ASN1 asn = new ASN1 (0x31);
|
||
|
foreach (ASN1 attr in signerInfo.AuthenticatedAttributes)
|
||
|
asn.Add (attr);
|
||
|
|
||
|
hash = ha.ComputeHash (asn.GetBytes ());
|
||
|
} else {
|
||
|
hash = ha.ComputeHash (contentInfo.Content[0].Value);
|
||
|
}
|
||
|
|
||
|
if (hash != null && signature != null) {
|
||
|
return r.VerifySignature (hash, signature);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
internal string OidToName (string oid)
|
||
|
{
|
||
|
switch (oid) {
|
||
|
case "1.3.14.3.2.26" :
|
||
|
return "SHA1";
|
||
|
case "1.2.840.113549.2.2" :
|
||
|
return "MD2";
|
||
|
case "1.2.840.113549.2.5" :
|
||
|
return "MD5";
|
||
|
case "2.16.840.1.101.3.4.1" :
|
||
|
return "SHA256";
|
||
|
case "2.16.840.1.101.3.4.2" :
|
||
|
return "SHA384";
|
||
|
case "2.16.840.1.101.3.4.3" :
|
||
|
return "SHA512";
|
||
|
default :
|
||
|
break;
|
||
|
}
|
||
|
// Unknown Oid
|
||
|
return oid;
|
||
|
}
|
||
|
|
||
|
internal ASN1 GetASN1 ()
|
||
|
{
|
||
|
// SignedData ::= SEQUENCE {
|
||
|
ASN1 signedData = new ASN1 (0x30);
|
||
|
// version Version -> Version ::= INTEGER
|
||
|
byte[] ver = { version };
|
||
|
signedData.Add (new ASN1 (0x02, ver));
|
||
|
// digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
|
||
|
ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
|
||
|
if (hashAlgorithm != null) {
|
||
|
string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
|
||
|
digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
|
||
|
}
|
||
|
|
||
|
// contentInfo ContentInfo,
|
||
|
ASN1 ci = contentInfo.ASN1;
|
||
|
signedData.Add (ci);
|
||
|
if (!signed && (hashAlgorithm != null)) {
|
||
|
if (mda) {
|
||
|
// Use authenticated attributes for signature
|
||
|
|
||
|
// Automatically add the contentType authenticated attribute
|
||
|
ASN1 ctattr = Attribute (Oid.contentType, ci[0]);
|
||
|
signerInfo.AuthenticatedAttributes.Add (ctattr);
|
||
|
|
||
|
// Automatically add the messageDigest authenticated attribute
|
||
|
HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
|
||
|
byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
|
||
|
ASN1 md = new ASN1 (0x30);
|
||
|
ASN1 mdattr = Attribute (Oid.messageDigest, md.Add (new ASN1 (0x04, idcHash)));
|
||
|
signerInfo.AuthenticatedAttributes.Add (mdattr);
|
||
|
} else {
|
||
|
// Don't use authenticated attributes for signature -- signature is content
|
||
|
RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (signerInfo.Key);
|
||
|
r.SetHashAlgorithm (hashAlgorithm);
|
||
|
HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
|
||
|
byte[] sig = ha.ComputeHash (ci[1][0].Value);
|
||
|
signerInfo.Signature = r.CreateSignature (sig);
|
||
|
}
|
||
|
signed = true;
|
||
|
}
|
||
|
|
||
|
// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
|
||
|
if (certs.Count > 0) {
|
||
|
ASN1 a0 = signedData.Add (new ASN1 (0xA0));
|
||
|
foreach (X509Certificate x in certs)
|
||
|
a0.Add (new ASN1 (x.RawData));
|
||
|
}
|
||
|
// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
|
||
|
if (crls.Count > 0) {
|
||
|
ASN1 a1 = signedData.Add (new ASN1 (0xA1));
|
||
|
foreach (byte[] crl in crls)
|
||
|
a1.Add (new ASN1 (crl));
|
||
|
}
|
||
|
// signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
|
||
|
ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
|
||
|
if (signerInfo.Key != null)
|
||
|
signerInfos.Add (signerInfo.ASN1);
|
||
|
return signedData;
|
||
|
}
|
||
|
|
||
|
public byte[] GetBytes ()
|
||
|
{
|
||
|
return GetASN1 ().GetBytes ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* SignerInfo ::= SEQUENCE {
|
||
|
* version Version,
|
||
|
* issuerAndSerialNumber IssuerAndSerialNumber,
|
||
|
* digestAlgorithm DigestAlgorithmIdentifier,
|
||
|
* authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
|
||
|
* digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
|
||
|
* encryptedDigest EncryptedDigest,
|
||
|
* unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
|
||
|
* }
|
||
|
*
|
||
|
* For version == 3 issuerAndSerialNumber may be replaced by ...
|
||
|
*/
|
||
|
public class SignerInfo {
|
||
|
|
||
|
private byte version;
|
||
|
private X509Certificate x509;
|
||
|
private string hashAlgorithm;
|
||
|
private AsymmetricAlgorithm key;
|
||
|
private ArrayList authenticatedAttributes;
|
||
|
private ArrayList unauthenticatedAttributes;
|
||
|
private byte[] signature;
|
||
|
private string issuer;
|
||
|
private byte[] serial;
|
||
|
private byte[] ski;
|
||
|
|
||
|
public SignerInfo ()
|
||
|
{
|
||
|
version = 1;
|
||
|
authenticatedAttributes = new ArrayList ();
|
||
|
unauthenticatedAttributes = new ArrayList ();
|
||
|
}
|
||
|
|
||
|
public SignerInfo (byte[] data)
|
||
|
: this (new ASN1 (data)) {}
|
||
|
|
||
|
// TODO: INCOMPLETE
|
||
|
public SignerInfo (ASN1 asn1) : this ()
|
||
|
{
|
||
|
if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5))
|
||
|
throw new ArgumentException ("Invalid SignedData");
|
||
|
|
||
|
// version Version
|
||
|
if (asn1[0][0].Tag != 0x02)
|
||
|
throw new ArgumentException ("Invalid version");
|
||
|
version = asn1[0][0].Value[0];
|
||
|
|
||
|
// issuerAndSerialNumber IssuerAndSerialNumber
|
||
|
ASN1 subjectIdentifierType = asn1 [0][1];
|
||
|
if ((subjectIdentifierType.Tag == 0x80) && (version == 3)) {
|
||
|
ski = subjectIdentifierType.Value;
|
||
|
}
|
||
|
else {
|
||
|
issuer = X501.ToString (subjectIdentifierType [0]);
|
||
|
serial = subjectIdentifierType [1].Value;
|
||
|
}
|
||
|
|
||
|
// digestAlgorithm DigestAlgorithmIdentifier
|
||
|
ASN1 digestAlgorithm = asn1 [0][2];
|
||
|
hashAlgorithm = ASN1Convert.ToOid (digestAlgorithm [0]);
|
||
|
|
||
|
// authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL
|
||
|
int n = 3;
|
||
|
ASN1 authAttributes = asn1 [0][n];
|
||
|
if (authAttributes.Tag == 0xA0) {
|
||
|
n++;
|
||
|
for (int i=0; i < authAttributes.Count; i++)
|
||
|
authenticatedAttributes.Add (authAttributes [i]);
|
||
|
}
|
||
|
|
||
|
// digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier
|
||
|
n++;
|
||
|
// ASN1 digestEncryptionAlgorithm = asn1 [0][n++];
|
||
|
// string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]);
|
||
|
|
||
|
// encryptedDigest EncryptedDigest
|
||
|
ASN1 encryptedDigest = asn1 [0][n++];
|
||
|
if (encryptedDigest.Tag == 0x04)
|
||
|
signature = encryptedDigest.Value;
|
||
|
|
||
|
// unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
|
||
|
ASN1 unauthAttributes = asn1 [0][n];
|
||
|
if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) {
|
||
|
for (int i=0; i < unauthAttributes.Count; i++)
|
||
|
unauthenticatedAttributes.Add (unauthAttributes [i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string IssuerName {
|
||
|
get { return issuer; }
|
||
|
}
|
||
|
|
||
|
public byte[] SerialNumber {
|
||
|
get {
|
||
|
if (serial == null)
|
||
|
return null;
|
||
|
return (byte[]) serial.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public byte[] SubjectKeyIdentifier {
|
||
|
get {
|
||
|
if (ski == null)
|
||
|
return null;
|
||
|
return (byte[]) ski.Clone ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ASN1 ASN1 {
|
||
|
get { return GetASN1(); }
|
||
|
}
|
||
|
|
||
|
public ArrayList AuthenticatedAttributes {
|
||
|
get { return authenticatedAttributes; }
|
||
|
}
|
||
|
|
||
|
public X509Certificate Certificate {
|
||
|
get { return x509; }
|
||
|
set { x509 = value; }
|
||
|
}
|
||
|
|
||
|
public string HashName {
|
||
|
get { return hashAlgorithm; }
|
||
|
set { hashAlgorithm = value; }
|
||
|
}
|
||
|
|
||
|
public AsymmetricAlgorithm Key {
|
||
|
get { return key; }
|
||
|
set { key = value; }
|
||
|
}
|
||
|
|
||
|
public byte[] Signature {
|
||
|
get {
|
||
|
if (signature == null)
|
||
|
return null;
|
||
|
return (byte[]) signature.Clone ();
|
||
|
}
|
||
|
|
||
|
set {
|
||
|
if (value != null) {
|
||
|
signature = (byte[]) value.Clone ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ArrayList UnauthenticatedAttributes {
|
||
|
get { return unauthenticatedAttributes; }
|
||
|
}
|
||
|
|
||
|
public byte Version {
|
||
|
get { return version; }
|
||
|
set { version = value; }
|
||
|
}
|
||
|
|
||
|
internal ASN1 GetASN1 ()
|
||
|
{
|
||
|
if ((key == null) || (hashAlgorithm == null))
|
||
|
return null;
|
||
|
byte[] ver = { version };
|
||
|
ASN1 signerInfo = new ASN1 (0x30);
|
||
|
// version Version -> Version ::= INTEGER
|
||
|
signerInfo.Add (new ASN1 (0x02, ver));
|
||
|
// issuerAndSerialNumber IssuerAndSerialNumber,
|
||
|
signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509));
|
||
|
// digestAlgorithm DigestAlgorithmIdentifier,
|
||
|
string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
|
||
|
signerInfo.Add (AlgorithmIdentifier (hashOid));
|
||
|
// authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
|
||
|
ASN1 aa = null;
|
||
|
if (authenticatedAttributes.Count > 0) {
|
||
|
aa = signerInfo.Add (new ASN1 (0xA0));
|
||
|
authenticatedAttributes.Sort(new SortedSet ());
|
||
|
foreach (ASN1 attr in authenticatedAttributes)
|
||
|
aa.Add (attr);
|
||
|
}
|
||
|
// digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
|
||
|
if (key is RSA) {
|
||
|
signerInfo.Add (AlgorithmIdentifier (PKCS7.Oid.rsaEncryption));
|
||
|
|
||
|
if (aa != null) {
|
||
|
// Calculate the signature here; otherwise it must be set from SignedData
|
||
|
RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key);
|
||
|
r.SetHashAlgorithm (hashAlgorithm);
|
||
|
byte[] tbs = aa.GetBytes ();
|
||
|
tbs [0] = 0x31; // not 0xA0 for signature
|
||
|
HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
|
||
|
byte[] tbsHash = ha.ComputeHash (tbs);
|
||
|
signature = r.CreateSignature (tbsHash);
|
||
|
}
|
||
|
}
|
||
|
else if (key is DSA) {
|
||
|
throw new NotImplementedException ("not yet");
|
||
|
}
|
||
|
else
|
||
|
throw new CryptographicException ("Unknown assymetric algorithm");
|
||
|
// encryptedDigest EncryptedDigest,
|
||
|
signerInfo.Add (new ASN1 (0x04, signature));
|
||
|
// unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
|
||
|
if (unauthenticatedAttributes.Count > 0) {
|
||
|
ASN1 ua = signerInfo.Add (new ASN1 (0xA1));
|
||
|
unauthenticatedAttributes.Sort(new SortedSet ());
|
||
|
foreach (ASN1 attr in unauthenticatedAttributes)
|
||
|
ua.Add (attr);
|
||
|
}
|
||
|
return signerInfo;
|
||
|
}
|
||
|
|
||
|
public byte[] GetBytes ()
|
||
|
{
|
||
|
return GetASN1 ().GetBytes ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class SortedSet : IComparer {
|
||
|
|
||
|
public int Compare (object x, object y)
|
||
|
{
|
||
|
if (x == null)
|
||
|
return (y == null) ? 0 : -1;
|
||
|
else if (y == null)
|
||
|
return 1;
|
||
|
|
||
|
ASN1 xx = x as ASN1;
|
||
|
ASN1 yy = y as ASN1;
|
||
|
|
||
|
if ((xx == null) || (yy == null)) {
|
||
|
throw new ArgumentException (("Invalid objects."));
|
||
|
}
|
||
|
|
||
|
byte[] xb = xx.GetBytes ();
|
||
|
byte[] yb = yy.GetBytes ();
|
||
|
|
||
|
for (int i = 0; i < xb.Length; i++) {
|
||
|
if (i == yb.Length)
|
||
|
break;
|
||
|
|
||
|
if (xb[i] == yb[i])
|
||
|
continue;
|
||
|
|
||
|
return (xb[i] < yb[i]) ? -1 : 1;
|
||
|
}
|
||
|
|
||
|
// The arrays are equal up to the shortest of them.
|
||
|
if (xb.Length > yb.Length)
|
||
|
return 1;
|
||
|
else if (xb.Length < yb.Length)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|