namespace com.hitrust.Security.Certificates { using Security; using System; using System.Collections; using System.IO; using System.Runtime.InteropServices; using System.Text; public class CertificateStore { public const string CAStore = "CA"; private IntPtr m_Handle; public const string MyStore = "My"; public const string RootStore = "Root"; public const string SoftwarePublisherStore = "SPC"; public const string TrustStore = "Trust"; public const string UnTrustedStore = "Disallowed"; public CertificateStore() { this.m_Handle = SspiProvider.CertOpenStore(new IntPtr(2), 0x10001, IntPtr.Zero, 0, null); if (this.m_Handle == IntPtr.Zero) { throw new CertificateException("An error occurs while creating the store."); } } public CertificateStore(CertificateStore store) { if (store == null) { throw new ArgumentNullException(); } this.InitStore(store.m_Handle, true); } public CertificateStore(IEnumerable certs) : this() { IEnumerator enumerator = certs.GetEnumerator(); while (enumerator.MoveNext()) { this.AddCertificate((Certificate)enumerator.Current); } } public CertificateStore(IntPtr handle) : this(handle, false) {} public CertificateStore(string store) : this(StoreLocation.Users, store) {} public CertificateStore(byte[] buffer, CertificateStoreType type) { DataBlob blob; IntPtr ptr; if (buffer == null) { throw new ArgumentNullException(); } blob = new DataBlob(); blob.cbData = buffer.Length; blob.pbData = Marshal.AllocHGlobal(blob.cbData); Marshal.Copy(buffer, 0, blob.pbData, buffer.Length); if (type == CertificateStoreType.Pkcs7Message) { ptr = new IntPtr(5); } else { ptr = new IntPtr(6); } this.m_Handle = SspiProvider.CertOpenStoreData(ptr, 0x10001, IntPtr.Zero, 0, ref blob); Marshal.FreeHGlobal(blob.pbData); if (this.m_Handle == IntPtr.Zero) { throw new CertificateException("An error occurs while opening the store."); } } public CertificateStore(StoreLocation location, string store) { if (store == null) { throw new ArgumentNullException("The name of the store cannot be a null reference."); } this.m_Handle = SspiProvider.CertOpenStore(new IntPtr(9), 0, IntPtr.Zero, (int)location, store); if (this.m_Handle == IntPtr.Zero) { throw new CertificateException("An error occurs while opening the specified store."); } } public CertificateStore(IntPtr handle, bool duplicate) { this.InitStore(handle, duplicate); } public void AddCertificate(Certificate cert) { if (cert == null) { throw new ArgumentNullException(); } if ((SspiProvider.CertAddCertificateContextToStore(this.Handle, cert.Handle, 1, IntPtr.Zero) == 0) && (Marshal.GetLastWin32Error() != -2146885627)) { throw new CertificateException("An error occurs while adding the certificate to the store."); } } public static CertificateStore CreateFromCerFile(string file) { if (file == null) { throw new ArgumentNullException("The filename cannot be a null reference."); } IntPtr handle = SspiProvider.CertOpenStore(new IntPtr(7), 0x10001, IntPtr.Zero, 0, file); if (handle == IntPtr.Zero) { throw new CertificateException("An error occurs while opening the specified store."); } return new CertificateStore(handle); } public static CertificateStore CreateFromPfxFile(byte[] file, string password) { return CreateFromPfxFile(file, password, false); } public static CertificateStore CreateFromPfxFile(string file, string password) { return CreateFromPfxFile(GetFileContents(file), password); } public static CertificateStore CreateFromPfxFile(byte[] file, string password, bool exportable) { return CreateFromPfxFile(file, password, exportable, KeysetLocation.Default); } public static CertificateStore CreateFromPfxFile(string file, string password, bool exportable) { return CreateFromPfxFile(GetFileContents(file), password, exportable); } public static CertificateStore CreateFromPfxFile(byte[] file, string password, bool exportable, KeysetLocation location) { if ((password == null) || (file == null)) { throw new ArgumentNullException("The arguments cannot be null references."); } DataBlob pPFX = new DataBlob { cbData = file.Length }; IntPtr destination = Marshal.AllocHGlobal(file.Length); Marshal.Copy(file, 0, destination, file.Length); pPFX.pbData = destination; try { if (SspiProvider.PFXIsPFXBlob(ref pPFX) == 0) { throw new CertificateException("The specified file is not a PFX file."); } if (SspiProvider.PFXVerifyPassword(ref pPFX, password, 0) == 0) { throw new CertificateException("The specified password is invalid."); } int dwFlags = (int)location; if (exportable) { dwFlags |= 1; } IntPtr handle = SspiProvider.PFXImportCertStore(ref pPFX, password, dwFlags); if (handle.Equals(IntPtr.Zero)) { throw new CertificateException("Unable to import the PFX file! [error code = " + Marshal.GetLastWin32Error() + "]"); } return new CertificateStore(handle); } finally { Marshal.FreeHGlobal(destination); } } public static CertificateStore CreateFromPfxFile(string file, string password, bool exportable, KeysetLocation location) { return CreateFromPfxFile(GetFileContents(file), password, exportable, location); } public void DeleteCertificate(Certificate cert) { if (cert == null) { throw new ArgumentNullException(); } Certificate certificate = this.FindCertificateByHash(cert.GetCertHash(HashType.SHA1), HashType.SHA1); if (certificate == null) { throw new CertificateException("The certificate could not be found in the store."); } if (SspiProvider.CertDeleteCertificateFromStore(certificate.Handle) == 0) { throw new CertificateException("An error occurs while removing the certificate from the store."); } } public Certificate[] EnumCertificates() { ArrayList list = new ArrayList(); for (Certificate certificate = this.FindCertificate(); certificate != null; certificate = this.FindCertificate(certificate)) { list.Add(certificate); } return (Certificate[])list.ToArray(typeof(Certificate)); } public Certificate[] EnumCertificates(string[] keyUsage) { ArrayList list = new ArrayList(); for (Certificate certificate = this.FindCertificateByUsage(keyUsage); certificate != null; certificate = this.FindCertificateByUsage(keyUsage, certificate)) { list.Add(certificate); } return (Certificate[])list.ToArray(typeof(Certificate)); } ~CertificateStore() { if (this.Handle != IntPtr.Zero) { SspiProvider.CertCloseStore(this.Handle, 0); this.m_Handle = IntPtr.Zero; } } public Certificate FindCertificate() { return this.FindCertificate(null); } public Certificate FindCertificate(Certificate previous) { IntPtr zero; if (previous == null) { zero = IntPtr.Zero; } else { zero = SspiProvider.CertDuplicateCertificateContext(previous.Handle); } IntPtr handle = SspiProvider.CertFindCertificateInStore(this.Handle, 1, 0, 0, IntPtr.Zero, zero); if (handle.Equals(IntPtr.Zero)) { return null; } return new Certificate(handle, this); } public Certificate FindCertificateByHash(byte[] hash) { return this.FindCertificateByHash(hash, HashType.SHA1); } public Certificate FindCertificateByHash(byte[] hash, HashType hashType) { int num; if (hash == null) { throw new ArgumentNullException(); } if (hashType == HashType.SHA1) { num = 0x10000; } else if (hashType == HashType.MD5) { num = 0x40000; } else { num = 0x10000; } DataBlob pvFindPara = new DataBlob { cbData = hash.Length, pbData = Marshal.AllocHGlobal(hash.Length) }; Marshal.Copy(hash, 0, pvFindPara.pbData, hash.Length); IntPtr handle = SspiProvider.CertFindDataBlobCertificateInStore(this.Handle, 0x10001, 0, num, ref pvFindPara, IntPtr.Zero); Marshal.FreeHGlobal(pvFindPara.pbData); if (handle == IntPtr.Zero) { return null; } return new Certificate(handle); } public Certificate FindCertificateByKeyIdentifier(byte[] keyID) { if (keyID == null) { throw new ArgumentNullException(); } if (keyID.Length == 0) { throw new ArgumentException(); } DataBlob pvFindPara = new DataBlob { cbData = keyID.Length, pbData = Marshal.AllocHGlobal(keyID.Length) }; Marshal.Copy(keyID, 0, pvFindPara.pbData, keyID.Length); IntPtr handle = SspiProvider.CertFindDataBlobCertificateInStore(this.Handle, 0x10001, 0, 0xf0000, ref pvFindPara, IntPtr.Zero); Marshal.FreeHGlobal(pvFindPara.pbData); if (handle == IntPtr.Zero) { return null; } return new Certificate(handle); } public Certificate FindCertificateBySubjectName(string name) { return this.FindCertificateBySubjectName(name, null); } public Certificate FindCertificateBySubjectName(string name, Certificate previous) { IntPtr ptr; if (name == null) { throw new ArgumentNullException(); } if (name.Length == 0) { throw new ArgumentException(); } IntPtr zero = IntPtr.Zero; if (previous == null) { ptr = IntPtr.Zero; } else { ptr = SspiProvider.CertDuplicateCertificateContext(previous.Handle); } DataBlob pvFindPara = new DataBlob(); if (SspiProvider.CertStrToName(0x10001, name, 3, IntPtr.Zero, IntPtr.Zero, ref pvFindPara.cbData, IntPtr.Zero) == 0) { throw new CertificateException("Could not encode the specified string. [is the string a valid X500 string?]"); } pvFindPara.pbData = Marshal.AllocHGlobal(pvFindPara.cbData); try { if (SspiProvider.CertStrToName(0x10001, name, 3, IntPtr.Zero, pvFindPara.pbData, ref pvFindPara.cbData, IntPtr.Zero) == 0) { throw new CertificateException("Could not encode the specified string."); } zero = SspiProvider.CertFindDataBlobCertificateInStore(this.Handle, 0x10001, 0, 0x20007, ref pvFindPara, ptr); } finally { Marshal.FreeHGlobal(pvFindPara.pbData); } if (zero == IntPtr.Zero) { return null; } return new Certificate(zero); } public Certificate FindCertificateBySubjectString(string subject) { return this.FindCertificateBySubjectString(subject, null); } public Certificate FindCertificateBySubjectString(string subject, Certificate previous) { IntPtr zero; if (subject == null) { throw new ArgumentNullException(); } if (subject.Length == 0) { throw new ArgumentException(); } if (previous == null) { zero = IntPtr.Zero; } else { zero = SspiProvider.CertDuplicateCertificateContext(previous.Handle); } IntPtr handle = SspiProvider.CertFindStringCertificateInStore(this.Handle, 0x10001, 0, 0x80007, subject, zero); if (handle == IntPtr.Zero) { return null; } return new Certificate(handle); } public Certificate FindCertificateByUsage(string[] keyUsage) { return this.FindCertificateByUsage(keyUsage, null); } public Certificate FindCertificateByUsage(string[] keyUsage, Certificate previous) { IntPtr zero; if (keyUsage == null) { throw new ArgumentNullException(); } if (keyUsage.Length == 0) { throw new ArgumentException(); } int cb = 0; for (int i = 0; i < keyUsage.Length; i++) { if ((keyUsage[i] == null) || (keyUsage[i].Length == 0)) { throw new ArgumentException(); } cb += keyUsage[i].Length + 1; } IntPtr hglobal = Marshal.AllocHGlobal(cb); IntPtr ptr = Marshal.AllocHGlobal((int)(IntPtr.Size * keyUsage.Length)); cb = 0; IntPtr destination = hglobal; for (int j = 0; j < keyUsage.Length; j++) { Marshal.Copy(Encoding.ASCII.GetBytes(keyUsage[j] + "\0"), 0, destination, keyUsage[j].Length + 1); Marshal.WriteIntPtr(ptr, j * IntPtr.Size, destination); destination = new IntPtr((hglobal.ToInt64() + keyUsage[j].Length) + 1L); } TrustListUsage pvFindPara = new TrustListUsage { cUsageIdentifier = keyUsage.Length, rgpszUsageIdentifier = ptr }; if (previous == null) { zero = IntPtr.Zero; } else { zero = SspiProvider.CertDuplicateCertificateContext(previous.Handle); } IntPtr handle = SspiProvider.CertFindUsageCertificateInStore(this.Handle, 0x10001, 0, 0xa0000, ref pvFindPara, zero); Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(hglobal); if (handle.Equals(IntPtr.Zero)) { return null; } return new Certificate(handle, this); } private byte[] GetCerBuffer(CertificateStoreType type) { byte[] buffer2; DataBlob pvSaveToPara = new DataBlob(); try { pvSaveToPara.cbData = 0; pvSaveToPara.pbData = IntPtr.Zero; if (SspiProvider.CertSaveStore(this.Handle, 0x10001, (int)type, 2, ref pvSaveToPara, 0) == 0) { throw new CertificateException("Unable to get the certificate data."); } pvSaveToPara.pbData = Marshal.AllocHGlobal(pvSaveToPara.cbData); if (SspiProvider.CertSaveStore(this.Handle, 0x10001, (int)type, 2, ref pvSaveToPara, 0) == 0) { throw new CertificateException("Unable to get the certificate data."); } byte[] destination = new byte[pvSaveToPara.cbData]; Marshal.Copy(pvSaveToPara.pbData, destination, 0, pvSaveToPara.cbData); buffer2 = destination; } finally { if (pvSaveToPara.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(pvSaveToPara.pbData); } } return buffer2; } internal static byte[] GetFileContents(string file) { byte[] buffer; if (file == null) { throw new ArgumentNullException(); } try { FileStream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); buffer = new byte[stream.Length]; for (int i = stream.Read(buffer, 0, buffer.Length); i < stream.Length; i += stream.Read(buffer, i, buffer.Length - i)) {} stream.Close(); } catch (Exception exception) { throw new IOException("An error occurs while reading from the file.", exception); } return buffer; } private byte[] GetPfxBuffer(string password, bool exportPrivateKeys) { byte[] buffer2; if (password == null) { throw new ArgumentNullException(); } DataBlob pPFX = new DataBlob(); try { pPFX.pbData = IntPtr.Zero; pPFX.cbData = 0; if (SspiProvider.PFXExportCertStoreEx(this.Handle, ref pPFX, password, IntPtr.Zero, exportPrivateKeys ? 4 : 0) == 0) { throw new CertificateException("Could not export the certificate store."); } pPFX.pbData = Marshal.AllocHGlobal(pPFX.cbData); if (SspiProvider.PFXExportCertStoreEx(this.Handle, ref pPFX, password, IntPtr.Zero, exportPrivateKeys ? 4 : 0) == 0) { throw new CertificateException("Could not export the certificate store."); } byte[] destination = new byte[pPFX.cbData]; Marshal.Copy(pPFX.pbData, destination, 0, destination.Length); buffer2 = destination; } finally { if (pPFX.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(pPFX.pbData); } } return buffer2; } protected void InitStore(IntPtr handle, bool duplicate) { if (handle == IntPtr.Zero) { throw new ArgumentException("Invalid certificate store handle!"); } if (duplicate) { this.m_Handle = SspiProvider.CertDuplicateStore(handle); } else { this.m_Handle = handle; } } private void SaveToFile(byte[] buffer, string filename) { if ((filename == null) || (buffer == null)) { throw new ArgumentNullException(); } try { FileStream stream = File.Open(filename, FileMode.CreateNew, FileAccess.Write, FileShare.None); stream.Write(buffer, 0, buffer.Length); stream.Close(); } catch (Exception exception) { throw new IOException("Could not write data to file.", exception); } } public byte[] ToCerBuffer(CertificateStoreType type) { return this.GetCerBuffer(type); } public void ToCerFile(string filename, CertificateStoreType type) { this.SaveToFile(this.GetCerBuffer(type), filename); } public byte[] ToPfxBuffer(string password, bool exportPrivateKeys) { return this.GetPfxBuffer(password, exportPrivateKeys); } public void ToPfxFile(string filename, string password, bool exportPrivateKeys) { this.SaveToFile(this.GetPfxBuffer(password, exportPrivateKeys), filename); } public IntPtr Handle { get { return this.m_Handle; } } } }