Find certificate by hash in Store C# using CryptoAPI
I would like to get certificate from Store using CryptoAPI P/Invoke. But I encountered some problems.
I can open store, but not find certificate. I can not understande why. The same code works on C++. I wo开发者_如何学JAVAuld like to use CryptoAPI, because .NET only enable to use key of certificates with key exportable marked "yes"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
namespace capp
{
public class Crypto
{
#region CONSTS
// #define CERT_COMPARE_SHIFT 16
public const Int32 CERT_COMPARE_SHIFT = 16;
// #define CERT_STORE_PROV_SYSTEM_W ((LPCSTR) 10)
public const Int32 CERT_STORE_PROV_SYSTEM_W = 10;
// #define CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W
public const Int32 CERT_STORE_PROV_SYSTEM = CERT_STORE_PROV_SYSTEM_W;
// #define CERT_SYSTEM_STORE_CURRENT_USER_ID 1
public const Int32 CERT_SYSTEM_STORE_CURRENT_USER_ID = 1;
// #define CERT_SYSTEM_STORE_LOCATION_SHIFT 16
public const Int32 CERT_SYSTEM_STORE_LOCATION_SHIFT = 16;
// #define CERT_SYSTEM_STORE_CURRENT_USER \
// (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT)
public const Int32 CERT_SYSTEM_STORE_CURRENT_USER =
CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT;
// #define CERT_COMPARE_SHA1_HASH 1
public const Int32 CERT_COMPARE_SHA1_HASH = 1;
// #define CERT_FIND_SHA1_HASH (CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT)
public const Int32 CERT_FIND_SHA1_HASH = (CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT);
// #define X509_ASN_ENCODING 0x00000001
public const Int32 X509_ASN_ENCODING = 0x00000001;
// #define PKCS_7_ASN_ENCODING 0x00010000
public const Int32 PKCS_7_ASN_ENCODING = 0x00010000;
// #define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
public const Int32 MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
#endregion
#region STRUCTS
// typedef struct _CRYPTOAPI_BLOB
// {
// DWORD cbData;
// BYTE *pbData;
// } CRYPT_HASH_BLOB, CRYPT_INTEGER_BLOB,
// CRYPT_OBJID_BLOB, CERT_NAME_BLOB;
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTOAPI_BLOB
{
public Int32 cbData;
public Byte[] pbData;
}
#endregion
#region FUNCTIONS (IMPORTS)
// HCERTSTORE WINAPI CertOpenStore(
// LPCSTR lpszStoreProvider,
// DWORD dwMsgAndCertEncodingType,
// HCRYPTPROV hCryptProv,
// DWORD dwFlags,
// const void* pvPara
// );
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertOpenStore(
Int32 lpszStoreProvider,
Int32 dwMsgAndCertEncodingType,
IntPtr hCryptProv,
Int32 dwFlags,
String pvPara
);
// BOOL WINAPI CertCloseStore(
// HCERTSTORE hCertStore,
// DWORD dwFlags
// );
[DllImport("Crypt32.dll", SetLastError = true)]
public static extern Boolean CertCloseStore(
IntPtr hCertStore,
Int32 dwFlags
);
// PCCERT_CONTEXT WINAPI CertFindCertificateInStore(
// HCERTSTORE hCertStore,
// DWORD dwCertEncodingType,
// DWORD dwFindFlags,
// DWORD dwFindType,
// const void* pvFindPara,
// PCCERT_CONTEXT pPrevCertContext
// );
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
Int32 dwCertEncodingType,
Int32 dwFindFlags,
Int32 dwFindType,
//String pvFindPara,
ref CRYPTOAPI_BLOB pvFindPara,
IntPtr pPrevCertContext
);
// BOOL WINAPI CertFreeCertificateContext(
// PCCERT_CONTEXT pCertContext
// );
[DllImport("Crypt32.dll", SetLastError = true)]
public static extern Boolean CertFreeCertificateContext(
IntPtr pCertContext
);
#endregion
}
class Program
{
const string MY = "MY";
static void Main(string[] args)
{
IntPtr hCertCntxt = IntPtr.Zero;
IntPtr hStore = IntPtr.Zero;
hStore = Crypto.CertOpenStore(Crypto.CERT_STORE_PROV_SYSTEM,
Crypto.MY_ENCODING_TYPE,
IntPtr.Zero,
Crypto.CERT_SYSTEM_STORE_CURRENT_USER,
MY);
Console.WriteLine("Store Handle:\t0x{0:X}", hStore.ToInt32());
String sha1Hex = "7a0b021806bffdb826205dac094030f8045d4daa";
// Convert to bin
int tam = sha1Hex.Length / 2;
byte[] sha1Bin = new byte[tam];
int aux = 0;
for (int i = 0; i < tam; ++i)
{
String str = sha1Hex.Substring(aux, 2);
sha1Bin[i] = (byte)Convert.ToInt32(str, 16);
aux = aux + 2;
}
Crypto.CRYPTOAPI_BLOB cryptBlob;
cryptBlob.cbData = sha1Bin.Length;
cryptBlob.pbData = sha1Bin;
if (hStore != IntPtr.Zero)
{
Console.WriteLine("Inside Store");
hCertCntxt = Crypto.CertFindCertificateInStore(
hStore,
Crypto.MY_ENCODING_TYPE,
0,
Crypto.CERT_FIND_SHA1_HASH,
ref cryptBlob,
IntPtr.Zero);
if (hCertCntxt != IntPtr.Zero)
Console.WriteLine("Certificate found!");
else
Console.WriteLine("Could not find ");
}
if (hCertCntxt != IntPtr.Zero)
Crypto.CertFreeCertificateContext(hCertCntxt);
if (hStore != IntPtr.Zero)
Crypto.CertCloseStore(hStore, 0);
}
}
}
Reference link to map CrytpoAPI to C# http://blogs.msdn.com/b/alejacma/archive/2007/11/23/p-invoking-cryptoapi-in-net-c-version.aspx
You can't just redefine CertFindCertificateInStore
to take a ref CRYPTOAPI_BLOB
.
There might be an easier way, but if you use these definitions:
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTOAPI_BLOB
{
public Int32 cbData;
public IntPtr pbData;
}
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
Int32 dwCertEncodingType,
Int32 dwFindFlags,
Int32 dwFindType,
IntPtr pvFindPara,
IntPtr pPrevCertContext
);
and call them like this:
Crypto.CRYPTOAPI_BLOB cryptBlob;
cryptBlob.cbData = sha1Bin.Length;
GCHandle h1 = default(GCHandle);
GCHandle h2 = default(GCHandle);
try{
h1 = GCHandle.Alloc(sha1Bin, GCHandleType.Pinned);
cryptBlob.pbData = h1.AddrOfPinnedObject();
h2 = GCHandle.Alloc(cryptBlob, GCHandleType.Pinned);
hCertCntxt = Crypto.CertFindCertificateInStore(
hStore,
Crypto.MY_ENCODING_TYPE,
0,
Crypto.CERT_FIND_SHA1_HASH,
h2.AddrOfPinnedObject(),
IntPtr.Zero);
}
finally{
if(h1!=default(GCHandle)) h1.Free();
if(h2!=default(GCHandle)) h2.Free();
}
, it should work.
If you are using .Net then why not use "System.Security.Cryptography" to do this?
Example:
X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = store.Certificates
.Find(X509FindType.FindByThumbprint, thumbprint, false)
.OfType<X509Certificate2>()
.FirstOrDefault();
精彩评论