namespace com.hitrust.Security.Certificates { using com.hitrust.Security; using com.hitrust.Security.Cryptography; using System; using System.Collections.Specialized; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; public class Certificate : ICloneable { internal CertificateInfo m_CertInfo; private CertificateChain m_Chain; internal CertificateContext m_Context; private IntPtr m_Handle; private CertificateStore m_Store; public Certificate(Certificate certificate) { this.m_Chain = null; if (certificate == null) { throw new ArgumentNullException(); } this.InitCertificate(certificate.Handle, true, null); } public Certificate(IntPtr handle) : this(handle, false) { } internal Certificate(IntPtr handle, CertificateStore store) { this.m_Chain = null; this.InitCertificate(handle, false, store); } public Certificate(IntPtr handle, bool duplicate) { this.m_Chain = null; this.InitCertificate(handle, duplicate, null); } public void AssociateWithPrivateKey(string pvkFile, string password) { this.AssociateWithPrivateKey(pvkFile, password, false); } public void AssociateWithPrivateKey(string pvkFile, string password, bool exportable) { try { if (!File.Exists(pvkFile)) { throw new FileNotFoundException("The PVK file could not be found."); } } catch (FileNotFoundException fnf) { throw fnf; } catch (Exception e) { throw new FileNotFoundException("The PVK file could not be found.", e); } byte[] buffer = new byte[0x18]; FileStream fs = File.Open(pvkFile, FileMode.Open, FileAccess.Read, FileShare.Read); fs.Read(buffer, 0, buffer.Length); if (BitConverter.ToUInt32(buffer, 0) != 0xb0b5f11e) { throw new CertificateException("The specified file is not a valid PVK file."); } int keytype = BitConverter.ToInt32(buffer, 8); int isEncrypted = BitConverter.ToInt32(buffer, 12); int saltLength = BitConverter.ToInt32(buffer, 0x10); int keyLen = BitConverter.ToInt32(buffer, 20); byte[] salt = new byte[saltLength]; byte[] blob = new byte[keyLen]; fs.Read(salt, 0, salt.Length); fs.Read(blob, 0, blob.Length); if (isEncrypted != 0) { if (password == null) { throw new ArgumentNullException(); } byte[] pass = Encoding.ASCII.GetBytes(password); byte[] key = new byte[salt.Length + password.Length]; Array.Copy(salt, 0, key, 0, salt.Length); Array.Copy(pass, 0, key, salt.Length, pass.Length); byte[] pkb = this.TryDecrypt(blob, 8, blob.Length - 8, key, 0x10); if (pkb == null) { pkb = this.TryDecrypt(blob, 8, blob.Length - 8, key, 5); if (pkb == null) { throw new CertificateException("The PVK file could not be decrypted. [wrong password?]"); } } Array.Copy(pkb, 0, blob, 8, pkb.Length); Array.Clear(pkb, 0, pkb.Length); Array.Clear(pass, 0, pass.Length); Array.Clear(key, 0, key.Length); } int hKey = 0; int flags = 0; if (exportable) { flags = 1; } int provider = 0; if (((SspiProvider.CryptAcquireContext(ref provider, "{48959A69-B181-4cdd-B135-7565701307C5}", null, 1, 8) == 0) && (Marshal.GetLastWin32Error() == -2146893809)) && (SspiProvider.CryptAcquireContext(ref provider, "{48959A69-B181-4cdd-B135-7565701307C5}", null, 1, 0) == 0)) { throw new CertificateException("Cannot acquire crypto service provider."); } if (SspiProvider.CryptImportKey(provider, blob, blob.Length, 0, flags, ref hKey) == 0) { throw new CertificateException("Could not import the private key from the PVK file."); } com.hitrust.Security.CRYPT_KEY_PROV_INFO kpi = new com.hitrust.Security.CRYPT_KEY_PROV_INFO { pwszContainerName = "{48959A69-B181-4cdd-B135-7565701307C5}", pwszProvName = null, dwProvType = 1, dwFlags = 0, cProvParam = 0, rgProvParam = IntPtr.Zero, dwKeySpec = keytype }; if (SspiProvider.CertSetCertificateContextProperty(this.Handle, 2, 0, ref kpi) == 0) { throw new CertificateException("Could not associate the private key with the certificate."); } SspiProvider.CryptDestroyKey(hKey); SspiProvider.CryptReleaseContext(provider, 0); Array.Clear(blob, 0, blob.Length); } private string BytesToString(byte[] buffer) { string ret = ""; for (int i = 0; i < buffer.Length; i++) { ret = ret + buffer[i].ToString("X2"); } return ret; } public object Clone() { return new Certificate(SspiProvider.CertDuplicateCertificateContext(this.Handle)); } internal byte[] ConvertIntToByteArray(int dwInput) { int i; int size = 0; byte[] temp = new byte[8]; if (dwInput == 0) { return new byte[1]; } int tempByte = dwInput; while (tempByte > 0) { temp[size] = (byte) (tempByte & 0xff); tempByte = tempByte >> 8; size++; } byte[] ret = new byte[size]; if (BitConverter.IsLittleEndian) { for (i = 0; i < size; i++) { ret[i] = temp[(size - i) - 1]; } return ret; } for (i = 0; i < size; i++) { ret[i] = temp[i]; } return ret; } private CertificateStore CreateCertStore(bool withParents) { CertificateStore store = new CertificateStore(); if (withParents) { Certificate[] c = this.GetCertificateChain().GetCertificates(); for (int i = 0; i < c.Length; i++) { store.AddCertificate(c[i]); } return store; } store.AddCertificate(this); return store; } public static Certificate CreateFromBase64String(string rawString) { if (rawString == null) { throw new ArgumentNullException("rawString"); } return CreateFromCerFile(Convert.FromBase64String(rawString)); } public static Certificate CreateFromCerFile(string file) { return CertificateStore.CreateFromCerFile(file).FindCertificate(); } public static Certificate CreateFromCerFile(byte[] file) { if (file == null) { throw new ArgumentNullException(); } return CreateFromCerFile(file, 0, file.Length); } public static Certificate CreateFromCerFile(byte[] file, int offset, int size) { if (file == null) { throw new ArgumentNullException(); } if ((offset < 0) || ((offset + size) > file.Length)) { throw new ArgumentOutOfRangeException(); } IntPtr data = Marshal.AllocHGlobal(size); Marshal.Copy(file, offset, data, size); IntPtr handle = SspiProvider.CertCreateCertificateContext(0x10001, data, size); Marshal.FreeHGlobal(data); if (handle == IntPtr.Zero) { throw new CertificateException("Unable to load the specified certificate."); } return new Certificate(handle); } public static Certificate CreateFromPemFile(byte[] file) { if (file == null) { throw new ArgumentNullException(); } string pemFile = Encoding.ASCII.GetString(file); string cert = GetCertString(pemFile, "CERTIFICATE"); if (cert == null) { cert = GetCertString(pemFile, "X509 CERTIFICATE"); if (cert == null) { throw new CertificateException("The specified PEM file does not contain a certificate."); } } return CreateFromCerFile(Convert.FromBase64String(cert)); } public static Certificate CreateFromPemFile(string filename) { return CreateFromPemFile(CertificateStore.GetFileContents(filename)); } public static Certificate CreateFromPfxFile(string file, string password) { return CertificateStore.CreateFromPfxFile(file, password).FindCertificate(); } public static Certificate CreateFromPfxFile(byte[] file, string password) { return CertificateStore.CreateFromPfxFile(file, password).FindCertificate(); } public static Certificate CreateFromPfxFile(string file, string password, bool exportable) { return CertificateStore.CreateFromPfxFile(file, password, exportable).FindCertificate(); } public static Certificate CreateFromPfxFile(byte[] file, string password, bool exportable) { return CertificateStore.CreateFromPfxFile(file, password, exportable).FindCertificate(); } public static Certificate CreateFromPfxFile(byte[] file, string password, bool exportable, KeysetLocation location) { return CertificateStore.CreateFromPfxFile(file, password, exportable, location).FindCertificate(); } public static Certificate CreateFromPfxFile(string file, string password, bool exportable, KeysetLocation location) { return CertificateStore.CreateFromPfxFile(file, password, exportable, location).FindCertificate(); } public static Certificate CreateFromX509Certificate(X509Certificate certificate) { if (certificate == null) { throw new ArgumentNullException(); } return CreateFromCerFile(certificate.GetRawCertData()); } public static object DecodeExtension(Extension extension, int oid, Type returnType) { return DecodeExtension(extension, new IntPtr(oid), returnType); } protected static object DecodeExtension(Extension extension, IntPtr oid, Type returnType) { object CS10000; if ((extension.EncodedValue == null) || (returnType == null)) { throw new ArgumentNullException(); } int size = 0; if (SspiProvider.CryptDecodeObject(0x10001, oid, extension.EncodedValue, extension.EncodedValue.Length, 0, IntPtr.Zero, ref size) == 0) { throw new CertificateException("Could not decode the extension."); } IntPtr buffer = Marshal.AllocHGlobal(size); try { if (SspiProvider.CryptDecodeObject(0x10001, oid, extension.EncodedValue, extension.EncodedValue.Length, 0, buffer, ref size) == 0) { throw new CertificateException("Could not decode the extension."); } CS10000 = Activator.CreateInstance(returnType, new object[] { buffer, size }); } catch (CertificateException ce) { throw ce; } catch (Exception e) { throw new CertificateException("Unable to instantiate the specified object type.", e); } finally { Marshal.FreeHGlobal(buffer); } return CS10000; } public static object DecodeExtension(Extension extension, string oid, Type returnType) { object CS10000; if (oid == null) { throw new ArgumentNullException("oid"); } IntPtr buffer = Marshal.StringToHGlobalAnsi(oid); try { CS10000 = DecodeExtension(extension, buffer, returnType); } finally { Marshal.FreeHGlobal(buffer); } return CS10000; } public virtual bool Equals(Certificate other) { if (other == null) { return false; } return (SspiProvider.CertCompareCertificate(0x10001, this.m_Context.pCertInfo, other.m_Context.pCertInfo) != 0); } public override bool Equals(object other) { bool CS10000; try { CS10000 = this.Equals((Certificate) other); } catch { try { CS10000 = this.Equals((X509Certificate) other); } catch { CS10000 = false; } } return CS10000; } public virtual bool Equals(X509Certificate other) { if (other == null) { return false; } return (other.GetCertHashString() == this.GetCertHashString()); } public void ExportPrivateKey(string pvkFile, string password) { byte[] salt; if (!this.HasPrivateKey()) { throw new CertificateException("The certificate does not have an associated private key."); } int flags = 0; int provider = 0; int keySpec = 0; int mustFree = 0; int privateKey = 0; int size = 0; if (!Environment.UserInteractive) { flags = 0x40; } if (SspiProvider.CryptAcquireCertificatePrivateKey(this.Handle, flags, IntPtr.Zero, ref provider, ref keySpec, ref mustFree) == 0) { throw new CertificateException("Could not acquire private key."); } if (SspiProvider.CryptGetUserKey(provider, keySpec, ref privateKey) == 0) { throw new CertificateException("Could not retrieve a handle of the private key."); } if ((SspiProvider.CryptExportKey(privateKey, 0, 7, 0, IntPtr.Zero, ref size) == 0) && (Marshal.GetLastWin32Error() != 0xea)) { throw new CertificateException("Could not export the private key."); } byte[] buffer = new byte[size]; if (SspiProvider.CryptExportKey(privateKey, 0, 7, 0, buffer, ref size) == 0) { throw new CertificateException("Could not export the private key."); } if (mustFree != 0) { SspiProvider.CryptReleaseContext(provider, 0); } uint magic = 0xb0b5f11e; int reserved = 0; int encrypted = (password == null) ? 0 : 1; if (encrypted == 0) { salt = new byte[0]; } else { salt = new byte[0x10]; new RNGCryptoServiceProvider().GetBytes(salt); byte[] key = Encoding.ASCII.GetBytes(password); SHA1 sha1 = SHA1.Create(); sha1.TransformBlock(salt, 0, salt.Length, salt, 0); sha1.TransformFinalBlock(key, 0, key.Length); key = new byte[0x10]; Array.Copy(sha1.Hash, 0, key, 0, key.Length); ICryptoTransform rc4 = RC4.Create().CreateEncryptor(key, null); rc4.TransformBlock(buffer, 8, buffer.Length - 8, buffer, 8); rc4.Dispose(); sha1.Clear(); } int saltlen = salt.Length; FileStream fs = null; try { fs = File.Open(pvkFile, FileMode.Create, FileAccess.Write, FileShare.Read); fs.Write(BitConverter.GetBytes(magic), 0, 4); fs.Write(BitConverter.GetBytes(reserved), 0, 4); fs.Write(BitConverter.GetBytes(keySpec), 0, 4); fs.Write(BitConverter.GetBytes(encrypted), 0, 4); fs.Write(BitConverter.GetBytes(saltlen), 0, 4); fs.Write(BitConverter.GetBytes(size), 0, 4); if (salt.Length > 0) { fs.Write(salt, 0, salt.Length); } fs.Write(buffer, 0, buffer.Length); } catch (IOException ioe) { throw ioe; } catch (Exception e) { throw new IOException("An error occurs while writing the file.", e); } finally { if (fs != null) { fs.Close(); } } } ~Certificate() { if (this.Handle != IntPtr.Zero) { SspiProvider.CertFreeCertificateContext(this.Handle); this.m_Handle = IntPtr.Zero; } } public Extension FindExtension(string oid) { if (oid == null) { throw new ArgumentNullException(); } IntPtr ret = SspiProvider.CertFindExtension(oid, this.m_CertInfo.cExtension, this.m_CertInfo.rgExtension); if (ret == IntPtr.Zero) { return null; } com.hitrust.Security.CERT_EXTENSION ce = (com.hitrust.Security.CERT_EXTENSION) Marshal.PtrToStructure(ret, typeof(com.hitrust.Security.CERT_EXTENSION)); Extension ext = new Extension(Marshal.PtrToStringAnsi(ce.pszObjId), ce.fCritical != 0, new byte[ce.ValuecbData]); Marshal.Copy(ce.ValuepbData, ext.EncodedValue, 0, ce.ValuecbData); return ext; } public byte[] GetCertHash() { return this.GetCertHash(HashType.SHA1); } public byte[] GetCertHash(HashType type) { byte[] ret; IntPtr hash = Marshal.AllocHGlobal(0x100); try { int size = 0x100; if (((SspiProvider.CertGetCertificateContextProperty(this.Handle, (int) type, hash, ref size) == 0) || (size <= 0)) || (size > 0x100)) { throw new CertificateException("An error occurs while retrieving the hash of the certificate."); } ret = new byte[size]; Marshal.Copy(hash, ret, 0, size); } catch (Exception e) { throw e; } finally { Marshal.FreeHGlobal(hash); } return ret; } public string GetCertHashString() { return this.GetCertHashString(HashType.SHA1); } public string GetCertHashString(HashType type) { return this.BytesToString(this.GetCertHash(type)); } private byte[] GetCertificateBuffer() { byte[] buffer = new byte[this.m_Context.cbCertEncoded]; Marshal.Copy(this.m_Context.pbCertEncoded, buffer, 0, this.m_Context.cbCertEncoded); return buffer; } public CertificateChain GetCertificateChain() { if (this.m_Chain == null) { this.m_Chain = new CertificateChain(this, this.Store); } return this.m_Chain; } internal CertificateInfo GetCertificateInfo() { return this.m_CertInfo; } private static string GetCertString(string cert, string delimiter) { int start = cert.IndexOf("-----BEGIN " + delimiter + "-----"); if (start < 0) { return null; } int end = cert.IndexOf("-----END " + delimiter + "-----", start); if (end < 0) { return null; } int sl = delimiter.Length + 0x10; int length = end - (start + sl); return cert.Substring(start + sl, length); } public DistinguishedName GetDistinguishedName() { return new DistinguishedName(this.m_CertInfo.SubjectpbData, this.m_CertInfo.SubjectcbData); } public DateTime GetEffectiveDate() { return DateTime.FromFileTime(this.m_CertInfo.NotBefore); } public StringCollection GetEnhancedKeyUsage() { StringCollection CS10000; StringCollection ret = new StringCollection(); int size = 0; SspiProvider.CertGetEnhancedKeyUsage(this.Handle, 0, IntPtr.Zero, ref size); if (size <= 0) { return ret; } IntPtr buffer = Marshal.AllocHGlobal(size); try { if (SspiProvider.CertGetEnhancedKeyUsage(this.Handle, 0, buffer, ref size) == 0) { throw new CertificateException("Could not obtain the enhanced key usage."); } TrustListUsage cu = (TrustListUsage) Marshal.PtrToStructure(buffer, typeof(TrustListUsage)); for (int i = 0; i < cu.cUsageIdentifier; i++) { IntPtr ip = Marshal.ReadIntPtr(cu.rgpszUsageIdentifier, i * IntPtr.Size); try { ret.Add(Marshal.PtrToStringAnsi(ip)); } catch { } } CS10000 = ret; } finally { Marshal.FreeHGlobal(buffer); } return CS10000; } public DateTime GetExpirationDate() { return DateTime.FromFileTime(this.m_CertInfo.NotAfter); } public Extension[] GetExtensions() { Extension[] ret = new Extension[this.m_CertInfo.cExtension]; int extSize = 8 + (IntPtr.Size * 2); IntPtr ptr = this.m_CertInfo.rgExtension; Type cest = typeof(com.hitrust.Security.CERT_EXTENSION); for (int i = 0; i < this.m_CertInfo.cExtension; i++) { com.hitrust.Security.CERT_EXTENSION ce = (com.hitrust.Security.CERT_EXTENSION) Marshal.PtrToStructure(ptr, cest); ret[i] = new Extension(Marshal.PtrToStringAnsi(ce.pszObjId), ce.fCritical != 0, new byte[ce.ValuecbData]); Marshal.Copy(ce.ValuepbData, ret[i].EncodedValue, 0, ce.ValuecbData); ptr = new IntPtr(ptr.ToInt64() + extSize); } return ret; } public string GetFormat() { return "X509"; } public override int GetHashCode() { byte[] hash = this.GetCertHash(); byte[] buffer = new byte[4]; if (hash.Length < buffer.Length) { Array.Copy(hash, 0, buffer, 0, hash.Length); } else { Array.Copy(hash, 0, buffer, 0, buffer.Length); } return BitConverter.ToInt32(buffer, 0); } public int GetIntendedKeyUsage() { IntPtr buffer = Marshal.AllocHGlobal(4); SspiProvider.CertGetIntendedKeyUsage(0x10001, this.m_Context.pCertInfo, buffer, 4); byte[] mb = new byte[4]; Marshal.Copy(buffer, mb, 0, 4); Marshal.FreeHGlobal(buffer); return BitConverter.ToInt32(mb, 0); } public string GetIssuerName() { int length = SspiProvider.CertGetNameString(this.Handle, 4, 0x10001, IntPtr.Zero, IntPtr.Zero, 0); if (length <= 0) { throw new CertificateException("An error occurs while requesting the issuer name."); } IntPtr name = Marshal.AllocHGlobal(length); SspiProvider.CertGetNameString(this.Handle, 4, 0x10001, IntPtr.Zero, name, length); string ret = Marshal.PtrToStringAnsi(name); Marshal.FreeHGlobal(name); return ret; } public string GetKeyAlgorithm() { return Marshal.PtrToStringAnsi(this.m_CertInfo.SignatureAlgorithmpszObjId); } public byte[] GetKeyAlgorithmParameters() { byte[] ret = new byte[this.m_CertInfo.SignatureAlgorithmParameterscbData]; if (ret.Length > 0) { Marshal.Copy(this.m_CertInfo.SignatureAlgorithmParameterspbData, ret, 0, ret.Length); } return ret; } public string GetKeyAlgorithmParametersString() { return this.BytesToString(this.GetKeyAlgorithmParameters()); } public byte[] GetKeyIdentifier() { int size = 0; SspiProvider.CertGetCertificateContextProperty(this.Handle, 20, (byte[]) null, ref size); byte[] ret = new byte[size]; SspiProvider.CertGetCertificateContextProperty(this.Handle, 20, ret, ref size); return ret; } public string GetName() { int size = 0; SspiProvider.CryptDecodeObject(0x10001, new IntPtr(20), this.m_CertInfo.SubjectpbData, this.m_CertInfo.SubjectcbData, 0, IntPtr.Zero, ref size); if (size <= 0) { throw new CertificateException("Unable to decode the name of the certificate."); } IntPtr buffer = IntPtr.Zero; string ret = null; try { buffer = Marshal.AllocHGlobal(size); if (SspiProvider.CryptDecodeObject(0x10001, new IntPtr(20), this.m_CertInfo.SubjectpbData, this.m_CertInfo.SubjectcbData, 0, buffer, ref size) == 0) { throw new CertificateException("Unable to decode the name of the certificate."); } IntPtr attPointer = SspiProvider.CertFindRDNAttr("2.5.4.3", buffer); if (attPointer == IntPtr.Zero) { attPointer = SspiProvider.CertFindRDNAttr("1.2.840.113549.1.9.2", buffer); } if (attPointer == IntPtr.Zero) { attPointer = SspiProvider.CertFindRDNAttr("2.5.4.10", buffer); } if (attPointer != IntPtr.Zero) { RdnAttribute att = (RdnAttribute) Marshal.PtrToStructure(attPointer, typeof(RdnAttribute)); ret = Marshal.PtrToStringUni(att.pbData, att.cbData / 2); } } catch (CertificateException ce) { throw ce; } catch (Exception e) { throw new CertificateException("Could not get certificate attributes.", e); } finally { if (buffer != IntPtr.Zero) { Marshal.FreeHGlobal(buffer); } } if (ret == null) { throw new CertificateException("Certificate does not have a name attribute."); } return ret; } public byte[] GetPublicKey() { byte[] key = new byte[this.m_CertInfo.SubjectPublicKeyInfoPublicKeycbData]; Marshal.Copy(this.m_CertInfo.SubjectPublicKeyInfoPublicKeypbData, key, 0, key.Length); return key; } public int GetPublicKeyLength() { return SspiProvider.CertGetPublicKeyLength(0x10001, new IntPtr(this.m_Context.pCertInfo.ToInt64() + 0x38L)); } public string GetPublicKeyString() { return this.BytesToString(this.GetPublicKey()); } public byte[] GetRawCertData() { byte[] ret = new byte[this.m_Context.cbCertEncoded]; Marshal.Copy(this.m_Context.pbCertEncoded, ret, 0, ret.Length); return ret; } public string GetRawCertDataString() { return this.BytesToString(this.GetRawCertData()); } public byte[] GetSerialNumber() { byte[] ret = new byte[this.m_CertInfo.SerialNumbercbData]; if (ret.Length > 0) { Marshal.Copy(this.m_CertInfo.SerialNumberpbData, ret, 0, ret.Length); } return ret; } public string GetSerialNumberString() { return this.BytesToString(this.GetSerialNumber()); } public static string[] GetValidUsages(Certificate[] certificates) { string[] CS10000; if (certificates == null) { throw new ArgumentNullException(); } IntPtr buffer = IntPtr.Zero; IntPtr certs = Marshal.AllocHGlobal((int) (certificates.Length * IntPtr.Size)); try { int i; for (i = 0; i < certificates.Length; i++) { if (certificates[i] == null) { throw new ArgumentException(); } Marshal.WriteIntPtr(certs, i * IntPtr.Size, certificates[i].Handle); } int count = 0; int bytes = 0; if (SspiProvider.CertGetValidUsages(certificates.Length, certs, ref count, buffer, ref bytes) == 0) { throw new CertificateException("Unable to get the valid usages."); } if (count == -1) { return null; } buffer = Marshal.AllocHGlobal(bytes); if (SspiProvider.CertGetValidUsages(certificates.Length, certs, ref count, buffer, ref bytes) == 0) { throw new CertificateException("Unable to get the valid usages."); } string[] ret = new string[count]; for (i = 0; i < count; i++) { ret[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(buffer, i * IntPtr.Size)); } CS10000 = ret; } finally { Marshal.FreeHGlobal(certs); if (buffer != IntPtr.Zero) { Marshal.FreeHGlobal(buffer); } } return CS10000; } public bool HasPrivateKey() { int handle = 0; int keyspec = 0; int free = 0; bool ret = false; if (SspiProvider.CryptAcquireCertificatePrivateKey(this.Handle, 0x44, IntPtr.Zero, ref handle, ref keyspec, ref free) != 0) { ret = true; } if (free != 0) { SspiProvider.CryptReleaseContext(handle, 0); } return ret; } private void InitCertificate(IntPtr handle, bool duplicate, CertificateStore store) { if (handle == IntPtr.Zero) { throw new ArgumentException("Invalid certificate handle!"); } if (duplicate) { this.m_Handle = SspiProvider.CertDuplicateCertificateContext(handle); } else { this.m_Handle = handle; } this.m_Context = (CertificateContext) Marshal.PtrToStructure(handle, typeof(CertificateContext)); this.m_CertInfo = (CertificateInfo) Marshal.PtrToStructure(this.m_Context.pCertInfo, typeof(CertificateInfo)); if (store == null) { this.m_Store = null; } else { this.m_Store = store; } } private void SaveToFile(byte[] buffer, string filename) { if (filename == null) { throw new ArgumentNullException(); } try { FileStream fs = File.Open(filename, FileMode.CreateNew, FileAccess.Write, FileShare.None); fs.Write(buffer, 0, buffer.Length); fs.Close(); } catch (Exception e) { throw new IOException("Could not write data to file.", e); } } public string ToBase64String() { byte[] cert = this.ToCerBuffer(); return Convert.ToBase64String(cert, 0, cert.Length); } public byte[] ToCerBuffer() { return this.GetCertificateBuffer(); } public void ToCerFile(string filename) { this.SaveToFile(this.GetCertificateBuffer(), filename); } public byte[] ToPemBuffer() { return Encoding.ASCII.GetBytes("-----BEGIN CERTIFICATE-----\r\n" + this.ToBase64String() + "\r\n-----END CERTIFICATE-----\r\n"); } public byte[] ToPfxBuffer(string password, bool withPrivateKeys, bool withParents) { return this.CreateCertStore(withParents).ToPfxBuffer(password, withPrivateKeys); } public void ToPfxFile(string filename, string password, bool withPrivateKeys, bool withParents) { this.CreateCertStore(withParents).ToPfxFile(filename, password, withPrivateKeys); } public override string ToString() { return base.GetType().FullName; } public string ToString(bool verbose) { if (verbose) { return ("CERTIFICATE:\r\n Format: X509\r\n Name: " + this.GetName() + "\r\n Issuing CA: " + this.GetIssuerName() + "\r\n Key Algorithm: " + this.GetKeyAlgorithm() + "\r\n Serial Number: " + this.GetSerialNumberString() + "\r\n Key Alogrithm Parameters: " + this.GetKeyAlgorithmParametersString() + "\r\n Public Key: " + this.GetPublicKeyString()); } return this.ToString(); } public X509Certificate ToX509() { return new X509Certificate(SspiProvider.CertDuplicateCertificateContext(this.Handle)); } private byte[] TryDecrypt(byte[] buffer, int offset, int length, byte[] password, int keyLen) { byte[] key = new byte[0x10]; Array.Copy(SHA1.Create().ComputeHash(password, 0, password.Length), 0, key, 0, keyLen); byte[] ret = RC4.Create().CreateDecryptor(key, null).TransformFinalBlock(buffer, offset, length); if ((((ret[0] != 0x52) || (ret[1] != 0x53)) || (ret[2] != 0x41)) || (ret[3] != 50)) { return null; } return ret; } public bool VerifyRevocation(byte[] crl) { if (crl == null) { throw new ArgumentNullException(); } IntPtr crlContextHandle = SspiProvider.CertCreateCRLContext(0x10001, crl, crl.Length); IntPtr crle = IntPtr.Zero; if (SspiProvider.CertFindCertificateInCRL(this.Handle, crlContextHandle, 0, IntPtr.Zero, ref crle) == 0) { throw new CertificateException("Unable to search the specified CRL for the certificate."); } return (crle == IntPtr.Zero); } public IntPtr Handle { get { return this.m_Handle; } } public bool IsCurrent { get { return (SspiProvider.CertVerifyTimeValidity(IntPtr.Zero, this.m_Context.pCertInfo) == 0); } } public RSACryptoServiceProvider PrivateKey { get { int flags = 0; int provider = 0; int keySpec = 0; int mustFree = 0; if (true) { flags = 0x40; } if (SspiProvider.CryptAcquireCertificatePrivateKey(this.Handle, flags, IntPtr.Zero, ref provider, ref keySpec, ref mustFree) == 0) { throw new CertificateException("Could not acquire private key."+ SspiProvider.GetLastError()); } if (mustFree != 0) { SspiProvider.CryptReleaseContext(provider, 0); } if (Environment.UserInteractive) { flags = 0; } else { flags = 0x40; } int length = 0; if ((SspiProvider.CryptFindCertificateKeyProvInfo(this.Handle, flags, IntPtr.Zero) == 0) || (SspiProvider.CertGetCertificateContextProperty(this.Handle, 2, (byte[])null, ref length) == 0)) { throw new CertificateException("Could not query the associated private key."); } IntPtr buffer = Marshal.AllocHGlobal(length); RSACryptoServiceProvider privateKey = null; try { SspiProvider.CertGetCertificateContextProperty(this.Handle, 2, buffer, ref length); com.hitrust.Security.CRYPT_KEY_PROV_INFO provInfo = (com.hitrust.Security.CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(buffer, typeof(com.hitrust.Security.CRYPT_KEY_PROV_INFO)); CspParameters cspParams = new CspParameters { KeyContainerName = provInfo.pwszContainerName, ProviderName = null, ProviderType = provInfo.dwProvType, KeyNumber = provInfo.dwKeySpec }; if ((provInfo.dwFlags & 0x20) != 0) { cspParams.Flags = CspProviderFlags.UseMachineKeyStore; } privateKey = new RSACryptoServiceProvider(cspParams); } catch (CertificateException e) { throw e; } catch (Exception e) { throw new CertificateException("An error occurs while accessing the certificate's private key.", e); } finally { Marshal.FreeHGlobal(buffer); } return privateKey; } } public RSACryptoServiceProvider PublicKey { get { IntPtr buffer = IntPtr.Zero; int provider = 0; int key = 0; RSACryptoServiceProvider publicKey = null; try { int flags = 0; if ((!Environment.UserInteractive && (Environment.OSVersion.Platform == PlatformID.Win32NT)) && (Environment.OSVersion.Version.Major >= 5)) { flags = 0x40; } if ((SspiProvider.CryptAcquireContext(ref provider, IntPtr.Zero, null, 1, flags) == 0) && (SspiProvider.CryptAcquireContext(ref provider, IntPtr.Zero, null, 1, flags | 8) == 0)) { throw new CertificateException("Could not acquire crypto context."); } com.hitrust.Security.CERT_PUBLIC_KEY_INFO pki = new com.hitrust.Security.CERT_PUBLIC_KEY_INFO(this.m_CertInfo); int size = 0; if (SspiProvider.CryptImportPublicKeyInfoEx(provider, 0x10001, ref pki, 0, 0, IntPtr.Zero, ref key) == 0) { throw new CertificateException("Could not obtain the handle of the public key."); } if (SspiProvider.CryptExportKey(key, 0, 6, 0, IntPtr.Zero, ref size) == 0) { throw new CertificateException("Could not get the size of the key."); } buffer = Marshal.AllocHGlobal(size); if (SspiProvider.CryptExportKey(key, 0, 6, 0, buffer, ref size) == 0) { throw new CertificateException("Could not export the key."); } PUBLIC_KEY_BLOB pkb = (PUBLIC_KEY_BLOB) Marshal.PtrToStructure(buffer, typeof(PUBLIC_KEY_BLOB)); if (pkb.magic != 0x31415352) { throw new CertificateException("This is not an RSA certificate."); } RSAParameters rsap = new RSAParameters { Exponent = this.ConvertIntToByteArray(pkb.pubexp) }; IntPtr modulus = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(typeof(PUBLIC_KEY_BLOB))); rsap.Modulus = new byte[pkb.bitlen / 8]; Marshal.Copy(modulus, rsap.Modulus, 0, rsap.Modulus.Length); Array.Reverse(rsap.Modulus); publicKey = new RSACryptoServiceProvider(new CspParameters { Flags = CspProviderFlags.UseMachineKeyStore }); publicKey.ImportParameters(rsap); } finally { if (key != 0) { SspiProvider.CryptDestroyKey(key); } if (provider != 0) { SspiProvider.CryptReleaseContext(provider, 0); } if (buffer != IntPtr.Zero) { Marshal.FreeHGlobal(buffer); } } return publicKey; } } internal CertificateStore Store { get { return this.m_Store; } set { if (this.m_Store != value) { this.m_Chain = null; } this.m_Store = value; } } public bool SupportsDataEncryption { get { return (this.HasPrivateKey() && ((this.GetIntendedKeyUsage() & 0x10) != 0)); } } public bool SupportsDigitalSignature { get { return (this.HasPrivateKey() && ((this.GetIntendedKeyUsage() & 0x80) != 0)); } } } }