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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|