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