X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMono.Security%2FMono.Security.X509%2FPKCS12.cs;h=0df82b90c314fd6cb2b52bd9b18437f99fed17f7;hb=d59143c0783ce8140176aeb36b70eafa99f749d4;hp=7e4d36e01883ee487757f3ca50ead8314a1ac66b;hpb=9f1a66f52e2e7e337ee4a9f31024202923469e37;p=mono.git diff --git a/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs b/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs index 7e4d36e0188..0df82b90c31 100755 --- a/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs +++ b/mcs/class/Mono.Security/Mono.Security.X509/PKCS12.cs @@ -2,14 +2,36 @@ // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax // // Author: -// Sebastien Pouliot (spouliot@motus.com) +// Sebastien Pouliot // // (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// (C) 2004 Novell (http://www.novell.com) // // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/) // See bouncycastle.txt for license. // +// +// 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.IO; @@ -21,7 +43,12 @@ using Mono.Security.Cryptography; namespace Mono.Security.X509 { - public class PKCS5 { +#if INSIDE_CORLIB + internal +#else + public +#endif + class PKCS5 { public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1"; public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3"; @@ -33,7 +60,45 @@ namespace Mono.Security.X509 { public PKCS5 () {} } - public class PKCS12 { +#if INSIDE_CORLIB + internal +#else + public +#endif + class PKCS9 { + + public const string friendlyName = "1.2.840.113549.1.9.20"; + public const string localKeyId = "1.2.840.113549.1.9.21"; + + public PKCS9 () {} + } + + + internal class SafeBag { + private string _bagOID; + private ASN1 _asn1; + + public SafeBag(string bagOID, ASN1 asn1) { + _bagOID = bagOID; + _asn1 = asn1; + } + + public string BagOID { + get { return _bagOID; } + } + + public ASN1 ASN1 { + get { return _asn1; } + } + } + + +#if INSIDE_CORLIB + internal +#else + public +#endif + class PKCS12 : ICloneable { public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1"; public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2"; @@ -202,20 +267,36 @@ namespace Mono.Security.X509 { } } - private int _version; + static private int recommendedIterationCount = 2000; + + //private int _version; private byte[] _password; private ArrayList _keyBags; private X509CertificateCollection _certs; + private bool _keyBagsChanged; + private bool _certsChanged; + private int _iterations; + private ArrayList _safeBags; + private RandomNumberGenerator _rng; // constructors public PKCS12 () { + _iterations = recommendedIterationCount; _keyBags = new ArrayList (); _certs = new X509CertificateCollection (); + _keyBagsChanged = false; + _certsChanged = false; + _safeBags = new ArrayList (); } - public PKCS12 (byte[] data) : this (data, null) {} + public PKCS12 (byte[] data) + : this () + { + Password = null; + Decode (data); + } /* * PFX ::= SEQUENCE { @@ -240,10 +321,22 @@ namespace Mono.Security.X509 { * bagAttributes SET OF PKCS12Attribute OPTIONAL * } */ - public PKCS12 (byte[] data, string password) : this () + public PKCS12 (byte[] data, string password) + : this () { Password = password; - + Decode (data); + } +#if NET_2_0 + public PKCS12 (byte[] data, byte[] password) + : this () + { + _password = password; + Decode (data); + } +#endif + private void Decode (byte[] data) + { ASN1 pfx = new ASN1 (data); if (pfx.Tag != 0x30) throw new ArgumentException ("invalid data"); @@ -251,10 +344,10 @@ namespace Mono.Security.X509 { ASN1 version = pfx [0]; if (version.Tag != 0x02) throw new ArgumentException ("invalid PFX version"); - _version = version.Value [0]; + //_version = version.Value [0]; PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]); - if (authSafe.ContentType != PKCS7.data) + if (authSafe.ContentType != PKCS7.Oid.data) throw new ArgumentException ("invalid authenticated safe"); // now that we know it's a PKCS#12 file, check the (optional) MAC @@ -268,7 +361,7 @@ namespace Mono.Security.X509 { if (mac.Tag != 0x30) throw new ArgumentException ("invalid MAC"); ASN1 macAlgorithm = mac [0]; - string macOid = ASN1Convert.ToOID (macAlgorithm [0]); + string macOid = ASN1Convert.ToOid (macAlgorithm [0]); if (macOid != "1.3.14.3.2.26") throw new ArgumentException ("unsupported HMAC"); byte[] macValue = mac [1].Value; @@ -277,24 +370,16 @@ namespace Mono.Security.X509 { if (macSalt.Tag != 0x04) throw new ArgumentException ("missing MAC salt"); - int iterations = 1; // default value + _iterations = 1; // default value if (macData.Count > 2) { ASN1 iters = macData [2]; if (iters.Tag != 0x02) throw new ArgumentException ("invalid MAC iteration"); - iterations = ASN1Convert.ToInt32 (iters); + _iterations = ASN1Convert.ToInt32 (iters); } - PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); - pd.HashName = "SHA1"; - pd.Password = _password; - pd.Salt = macSalt.Value; - pd.IterationCount = iterations; - - HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create (); - hmac.Key = pd.DeriveMAC (20); byte[] authSafeData = authSafe.Content [0].Value; - byte[] calculatedMac = hmac.ComputeHash (authSafeData, 0, authSafeData.Length); + byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData); if (!Compare (macValue, calculatedMac)) throw new CryptographicException ("Invalid MAC - file may have been tampered!"); } @@ -304,7 +389,7 @@ namespace Mono.Security.X509 { for (int i=0; i < authenticatedSafe.Count; i++) { PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]); switch (ci.ContentType) { - case PKCS7.data: + case PKCS7.Oid.data: // unencrypted (by PKCS#12) ASN1 safeContents = new ASN1 (ci.Content [0].Value); for (int j=0; j < safeContents.Count; j++) { @@ -312,7 +397,7 @@ namespace Mono.Security.X509 { ReadSafeBag (safeBag); } break; - case PKCS7.encryptedData: + case PKCS7.Oid.encryptedData: // password encrypted PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]); ASN1 decrypted = new ASN1 (Decrypt (ed)); @@ -321,7 +406,7 @@ namespace Mono.Security.X509 { ReadSafeBag (safeBag); } break; - case PKCS7.envelopedData: + case PKCS7.Oid.envelopedData: // public key encrypted throw new NotImplementedException ("public key encrypted"); default: @@ -335,6 +420,7 @@ namespace Mono.Security.X509 { if (_password != null) { Array.Clear (_password, 0, _password.Length); } + _password = null; } // properties @@ -352,12 +438,86 @@ namespace Mono.Security.X509 { } } + public int IterationCount { + get { return _iterations; } + set { _iterations = value; } + } + public ArrayList Keys { - get { return _keyBags; } + get { + if (_keyBagsChanged) { + _keyBags.Clear (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); + break; + case 0x30: + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p)); + break; + case 0x30: + _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey)); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + } + _keyBagsChanged = false; + } + return ArrayList.ReadOnly(_keyBags); + } } public X509CertificateCollection Certificates { - get { return _certs; } + get { + if (_certsChanged) { + _certs.Clear (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); + _certs.Add (new X509Certificate (cert.Content [0].Value)); + } + } + _certsChanged = false; + } + return _certs; + } + } + + internal RandomNumberGenerator RNG { + get { + if (_rng == null) + _rng = RandomNumberGenerator.Create (); + return _rng; + } } // private methods @@ -375,7 +535,7 @@ namespace Mono.Security.X509 { return compare; } - public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) + private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount) { string algorithm = null; int keyLength = 8; // 64 bits (default) @@ -463,8 +623,23 @@ namespace Mono.Security.X509 { sa.IV = pd.DeriveIV (ivLength); sa.Mode = CipherMode.CBC; } - ICryptoTransform ct = sa.CreateDecryptor (); - return ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length); + return sa; + } + + public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) + { + SymmetricAlgorithm sa = null; + byte[] result = null; + try { + sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount); + ICryptoTransform ct = sa.CreateDecryptor (); + result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length); + } + finally { + if (sa != null) + sa.Clear (); + } + return result; } public byte[] Decrypt (PKCS7.EncryptedData ed) @@ -475,6 +650,16 @@ namespace Mono.Security.X509 { ed.EncryptedContent); } + public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) + { + byte[] result = null; + using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) { + ICryptoTransform ct = sa.CreateEncryptor (); + result = ct.TransformFinalBlock (data, 0, data.Length); + } + return result; + } + private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) { byte[] privateKey = pki.PrivateKey; @@ -503,7 +688,7 @@ namespace Mono.Security.X509 { throw new ArgumentException ("invalid safeBag id"); ASN1 bagValue = safeBag [1]; - string oid = ASN1Convert.ToOID (bagId); + string oid = ASN1Convert.ToOid (bagId); switch (oid) { case keyBag: // NEED UNIT TEST @@ -534,11 +719,272 @@ namespace Mono.Security.X509 { default: throw new ArgumentException ("unknown safeBag oid"); } + + if (safeBag.Count > 2) { + ASN1 bagAttributes = safeBag [2]; + if (bagAttributes.Tag != 0x31) + throw new ArgumentException ("invalid safeBag attributes id"); + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes[i]; + + if (pkcs12Attribute.Tag != 0x30) + throw new ArgumentException ("invalid PKCS12 attributes id"); + + ASN1 attrId = pkcs12Attribute [0]; + if (attrId.Tag != 0x06) + throw new ArgumentException ("invalid attribute id"); + + string attrOid = ASN1Convert.ToOid (attrId); + + ASN1 attrValues = pkcs12Attribute[1]; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues[j]; + + switch (attrOid) { + case PKCS9.friendlyName: + if (attrValue.Tag != 0x1e) + throw new ArgumentException ("invalid attribute value id"); + break; + case PKCS9.localKeyId: + if (attrValue.Tag != 0x04) + throw new ArgumentException ("invalid attribute value id"); + break; + default: + // Unknown OID -- don't check Tag + break; + } + } + } + } + + _safeBags.Add (new SafeBag(oid, safeBag)); } - static private int recommendedIterationCount = 2000; + private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); + if (aa is RSA) { + pki.Algorithm = "1.2.840.113549.1.1.1"; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); + } + else if (aa is DSA) { + pki.Algorithm = null; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); + } + else + throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); + + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (); + epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC; + epki.IterationCount = _iterations; + epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ()); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag)); + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (new ASN1 (epki.GetBytes ())); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } - /* + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (); + if (aa is RSA) { + pki.Algorithm = "1.2.840.113549.1.1.1"; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa); + } + else if (aa is DSA) { + pki.Algorithm = null; + pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa); + } + else + throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ()); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (keyBag)); + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (new ASN1 (pki.GetBytes ())); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) + { + ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData); + + PKCS7.ContentInfo ci = new PKCS7.ContentInfo (); + ci.ContentType = x509Certificate; + ci.Content.Add (encapsulatedCertificate); + + ASN1 bagValue = new ASN1 (0xA0); + bagValue.Add (ci.ASN1); + + ASN1 safeBag = new ASN1 (0x30); + safeBag.Add (ASN1Convert.FromOid (certBag)); + safeBag.Add (bagValue); + + if (attributes != null) { + ASN1 bagAttributes = new ASN1 (0x31); + IDictionaryEnumerator de = attributes.GetEnumerator (); + + while (de.MoveNext ()) { + string oid = (string)de.Key; + switch (oid) { + case PKCS9.friendlyName: + ArrayList names = (ArrayList)de.Value; + if (names.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] name in names) { + ASN1 attrValue = new ASN1 (0x1e); + attrValue.Value = name; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + case PKCS9.localKeyId: + ArrayList keys = (ArrayList)de.Value; + if (keys.Count > 0) { + ASN1 pkcs12Attribute = new ASN1 (0x30); + pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId)); + ASN1 attrValues = new ASN1 (0x31); + foreach (byte[] key in keys) { + ASN1 attrValue = new ASN1 (0x04); + attrValue.Value = key; + attrValues.Add (attrValue); + } + pkcs12Attribute.Add (attrValues); + bagAttributes.Add (pkcs12Attribute); + } + break; + default: + break; + } + } + + if (bagAttributes.Count > 0) { + safeBag.Add (bagAttributes); + } + } + + return safeBag; + } + + private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) + { + PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes (); + pd.HashName = "SHA1"; + pd.Password = password; + pd.Salt = salt; + pd.IterationCount = iterations; + + HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create (); + hmac.Key = pd.DeriveMAC (20); + return hmac.ComputeHash (data, 0, data.Length); + } + + /* * SafeContents ::= SEQUENCE OF SafeBag * * SafeBag ::= SEQUENCE { @@ -549,25 +995,143 @@ namespace Mono.Security.X509 { */ public byte[] GetBytes () { - PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.data); - // TODO (incomplete) + ASN1 safeBagSequence = new ASN1 (0x30); + + // Sync Safe Bag list since X509CertificateCollection may be updated + ArrayList scs = new ArrayList (); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value); + scs.Add (new X509Certificate (cert.Content [0].Value)); + } + } + + ArrayList addcerts = new ArrayList (); + ArrayList removecerts = new ArrayList (); - byte[] salt = new byte [20]; - RandomNumberGenerator rng = RandomNumberGenerator.Create (); - rng.GetBytes (salt); + foreach (X509Certificate c in Certificates) { + bool found = false; + foreach (X509Certificate lc in scs) { + if (Compare (c.RawData, lc.RawData)) { + found = true; + } + } + + if (!found) { + addcerts.Add (c); + } + } + foreach (X509Certificate c in scs) { + bool found = false; + + foreach (X509Certificate lc in Certificates) { + if (Compare (c.RawData, lc.RawData)) { + found = true; + } + } + + if (!found) { + removecerts.Add (c); + } + } + + foreach (X509Certificate c in removecerts) { + RemoveCertificate (c); + } + + foreach (X509Certificate c in addcerts) { + AddCertificate (c); + } + // Sync done + + if (_safeBags.Count > 0) { + ASN1 certsSafeBag = new ASN1 (0x30); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + certsSafeBag.Add (sb.ASN1); + } + } + + if (certsSafeBag.Count > 0) { + byte[] certsSalt = new byte [8]; + RNG.GetBytes (certsSalt); + + ASN1 seqParams = new ASN1 (0x30); + seqParams.Add (new ASN1 (0x04, certsSalt)); + seqParams.Add (ASN1Convert.FromInt32 (_iterations)); + + ASN1 seqPbe = new ASN1 (0x30); + seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC)); + seqPbe.Add (seqParams); + + byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ()); + ASN1 encryptedCerts = new ASN1 (0x80, encrypted); + + ASN1 seq = new ASN1 (0x30); + seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data)); + seq.Add (seqPbe); + seq.Add (encryptedCerts); + + ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 }); + ASN1 encData = new ASN1 (0x30); + encData.Add (certsVersion); + encData.Add (seq); + + ASN1 certsContent = new ASN1 (0xA0); + certsContent.Add (encData); + + PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData); + bag.Content = certsContent; + safeBagSequence.Add (bag.ASN1); + } + } + + if (_safeBags.Count > 0) { + ASN1 safeContents = new ASN1 (0x30); + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || + sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + safeContents.Add (sb.ASN1); + } + } + if (safeContents.Count > 0) { + ASN1 content = new ASN1 (0xA0); + content.Add (new ASN1 (0x04, safeContents.GetBytes ())); + + PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data); + keyBag.Content = content; + safeBagSequence.Add (keyBag.ASN1); + } + } + + + ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ()); + ASN1 ci = new ASN1 (0xA0); + ci.Add (encapsulates); + PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data); + authSafe.Content = ci; + ASN1 macData = new ASN1 (0x30); - byte[] macValue = null; - if (macValue != null) { + if (_password != null) { // only for password based encryption + byte[] salt = new byte [20]; + RNG.GetBytes (salt); + byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value); + ASN1 oidSeq = new ASN1 (0x30); + oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1 + oidSeq.Add (new ASN1 (0x05)); + ASN1 mac = new ASN1 (0x30); - mac.Add (ASN1Convert.FromOID ("1.3.14.3.2.26")); // SHA1 + mac.Add (oidSeq); mac.Add (new ASN1 (0x04, macValue)); macData.Add (mac); macData.Add (new ASN1 (0x04, salt)); - macData.Add (ASN1Convert.FromInt32 (recommendedIterationCount)); + macData.Add (ASN1Convert.FromInt32 (_iterations)); } ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 }); @@ -575,7 +1139,7 @@ namespace Mono.Security.X509 { ASN1 pfx = new ASN1 (0x30); pfx.Add (version); pfx.Add (authSafe.ASN1); - if (macValue != null) { + if (macData.Count > 0) { // only for password based encryption pfx.Add (macData); } @@ -583,6 +1147,567 @@ namespace Mono.Security.X509 { return pfx.GetBytes (); } + public void AddCertificate (X509Certificate cert) + { + AddCertificate (cert, null); + } + + public void AddCertificate (X509Certificate cert, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate c = new X509Certificate (crt.Content [0].Value); + if (Compare (cert.RawData, c.RawData)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes))); + _certsChanged = true; + } + } + + public void RemoveCertificate (X509Certificate cert) + { + RemoveCertificate (cert, null); + } + + public void RemoveCertificate (X509Certificate cert, IDictionary attrs) + { + int certIndex = -1; + + for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate c = new X509Certificate (crt.Content [0].Value); + if (Compare (cert.RawData, c.RawData)) { + if (attrs != null) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + int bagAttributesFound = 0; + for (int j = 0; j < bagAttributes.Count; j++) { + ASN1 pkcs12Attribute = bagAttributes [j]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int k = 0; k < attrValues.Count; k++) { + ASN1 attrValue = attrValues [k]; + byte[] value = (byte[])dattrValues [k]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + certIndex = i; + } + } + } else { + certIndex = i; + } + } + } + } + + if (certIndex != -1) { + _safeBags.RemoveAt (certIndex); + _certsChanged = true; + } + } + + private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2) + { + bool result = a1.KeyExchangeAlgorithm.Equals (a2.KeyExchangeAlgorithm); + result = result && a1.KeySize == a2.KeySize; + + KeySizes[] keysizes = a2.LegalKeySizes; + if (a1.LegalKeySizes.Length != keysizes.Length) + return false; + + for (int i = 0; i < a1.LegalKeySizes.Length; i++) { + result = result && CompareKeySizes (a1.LegalKeySizes [i], keysizes [i]); + } + + result = result && a1.SignatureAlgorithm == a2.SignatureAlgorithm; + + return result; + } + + private bool CompareKeySizes (KeySizes k1, KeySizes k2) + { + bool result = k1.MaxSize == k2.MaxSize; + result = result && k1.MinSize == k2.MinSize; + result = result && k1.SkipSize == k2.SkipSize; + return result; + } + + public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) + { + AddPkcs8ShroudedKeyBag (aa, null); + } + + public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa , saa)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes))); + _keyBagsChanged = true; + } + } + + public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa) + { + int aaIndex = -1; + + for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (decrypted, 0, decrypted.Length); + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + aaIndex = i; + } + } + } + + if (aaIndex != -1) { + _safeBags.RemoveAt (aaIndex); + _keyBagsChanged = true; + } + } + + public void AddKeyBag (AsymmetricAlgorithm aa) + { + AddKeyBag (aa, null); + } + + public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes) + { + bool found = false; + + for (int i = 0; !found && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (keyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + found = true; + } + } + } + + if (!found) { + _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes))); + _keyBagsChanged = true; + } + } + + public void RemoveKeyBag (AsymmetricAlgorithm aa) + { + int aaIndex = -1; + + for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) { + SafeBag sb = (SafeBag)_safeBags [i]; + + if (sb.BagOID.Equals (keyBag)) { + ASN1 bagValue = sb.ASN1 [1]; + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + + AsymmetricAlgorithm saa = null; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + Array.Clear (privateKey, 0, privateKey.Length); + throw new CryptographicException ("Unknown private key format"); + } + + Array.Clear (privateKey, 0, privateKey.Length); + + if (CompareAsymmetricAlgorithm (aa, saa)) { + aaIndex = i; + } + } + } + + if (aaIndex != -1) { + _safeBags.RemoveAt (aaIndex); + _keyBagsChanged = true; + } + } + + + public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs) + { + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + int bagAttributesFound = 0; + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + byte[] value = (byte[])dattrValues [j]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + ASN1 bagValue = safeBag [1]; + AsymmetricAlgorithm aa = null; + if (sb.BagOID.Equals (keyBag)) { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + return aa; + } + } + } + } + + return null; + } + + public X509Certificate GetCertificate (IDictionary attrs) + { + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + int bagAttributesFound = 0; + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string ao = ASN1Convert.ToOid (attrId); + ArrayList dattrValues = (ArrayList)attrs [ao]; + + if (dattrValues != null) { + ASN1 attrValues = pkcs12Attribute [1]; + + if (dattrValues.Count == attrValues.Count) { + int attrValuesFound = 0; + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + byte[] value = (byte[])dattrValues [j]; + + if (Compare (value, attrValue.Value)) { + attrValuesFound += 1; + } + } + if (attrValuesFound == attrValues.Count) { + bagAttributesFound += 1; + } + } + } + } + if (bagAttributesFound == bagAttributes.Count) { + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + return new X509Certificate (crt.Content [0].Value); + } + } + } + } + + return null; + } + + public IDictionary GetAttributes (AsymmetricAlgorithm aa) + { + IDictionary result = new Hashtable (); + + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + ASN1 safeBag = sb.ASN1; + + ASN1 bagValue = safeBag [1]; + AsymmetricAlgorithm saa = null; + if (sb.BagOID.Equals (keyBag)) { + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) { + PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value); + byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData); + PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted); + byte[] privateKey = pki.PrivateKey; + switch (privateKey [0]) { + case 0x02: + DSAParameters p = new DSAParameters (); // FIXME + saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p); + break; + case 0x30: + saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey); + break; + default: + break; + } + Array.Clear (privateKey, 0, privateKey.Length); + Array.Clear (decrypted, 0, decrypted.Length); + } + + if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + string aOid = ASN1Convert.ToOid (attrId); + ArrayList aValues = new ArrayList (); + + ASN1 attrValues = pkcs12Attribute [1]; + + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + aValues.Add (attrValue.Value); + } + result.Add (aOid, aValues); + } + } + } + } + } + + return result; + } + + public IDictionary GetAttributes (X509Certificate cert) + { + IDictionary result = new Hashtable (); + + foreach (SafeBag sb in _safeBags) { + if (sb.BagOID.Equals (certBag)) { + ASN1 safeBag = sb.ASN1; + ASN1 bagValue = safeBag [1]; + PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value); + X509Certificate xc = new X509Certificate (crt.Content [0].Value); + + if (Compare (cert.RawData, xc.RawData)) { + if (safeBag.Count == 3) { + ASN1 bagAttributes = safeBag [2]; + + for (int i = 0; i < bagAttributes.Count; i++) { + ASN1 pkcs12Attribute = bagAttributes [i]; + ASN1 attrId = pkcs12Attribute [0]; + + string aOid = ASN1Convert.ToOid (attrId); + ArrayList aValues = new ArrayList (); + + ASN1 attrValues = pkcs12Attribute [1]; + + for (int j = 0; j < attrValues.Count; j++) { + ASN1 attrValue = attrValues [j]; + aValues.Add (attrValue.Value); + } + result.Add (aOid, aValues); + } + } + } + } + } + + return result; + } + + public void SaveToFile (string filename) + { + if (filename == null) + throw new ArgumentNullException ("filename"); + + using (FileStream fs = File.OpenWrite (filename)) { + byte[] data = GetBytes (); + fs.Write (data, 0, data.Length); + fs.Flush (); + fs.Close (); + } + } + + public object Clone () + { + PKCS12 clone = null; + if (_password != null) { + clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password)); + } else { + clone = new PKCS12 (GetBytes ()); + } + clone.IterationCount = this.IterationCount; + + return clone; + } + // static methods static private byte[] LoadFile (string filename) @@ -608,8 +1733,6 @@ namespace Mono.Security.X509 { { if (filename == null) throw new ArgumentNullException ("filename"); - if (password == null) - throw new ArgumentNullException ("password"); return new PKCS12 (LoadFile (filename), password); }