Last active
April 24, 2019 12:03
-
-
Save fperana/826fd2f749b129b5fcfc7ded6405b47b to your computer and use it in GitHub Desktop.
Get Subject's Common Name from Certificates stored on Smart Card [Delphi + WinCryptographyAPIs]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit Unit1; | |
interface | |
uses | |
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, | |
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; | |
type | |
TForm1 = class(TForm) | |
Button1: TButton; | |
Memo1: TMemo; | |
procedure Button1Click(Sender: TObject); | |
private | |
{ Private declarations } | |
public | |
{ Public declarations } | |
end; | |
var | |
Form1: TForm1; | |
implementation | |
{$R *.dfm} | |
uses | |
Winapi.NCrypt | |
, Winapi.WinCrypt | |
; | |
procedure TForm1.Button1Click(Sender: TObject); | |
var | |
hProvider: NCRYPT_PROV_HANDLE; | |
ppKeyName: PNCryptKeyName; | |
ppEnumState: PVOID; | |
hKey: NCRYPT_KEY_HANDLE; | |
cbResult: DWORD; | |
aOutput: TBytes; | |
ppCertContext: PCertContext; | |
aOutput2: TBytes; | |
hCS: HCERTSTORE; | |
pCNV: PCertNameValue; | |
begin | |
Memo1.Clear; | |
if NCryptOpenStorageProvider(hProvider, | |
MS_SMART_CARD_KEY_STORAGE_PROVIDER, | |
0) = ERROR_SUCCESS then | |
try | |
// {Attempt to use NCryptGetProperty to get a CertStore, but why should I get one?!} | |
// if NCryptGetProperty(hProvider, | |
// NCRYPT_USER_CERTSTORE_PROPERTY, | |
// nil, | |
// 0, | |
// cbResult, | |
// NCRYPT_SILENT_FLAG) = ERROR_SUCCESS then | |
// begin | |
// if NCryptGetProperty(hProvider, | |
// NCRYPT_USER_CERTSTORE_PROPERTY, | |
// @hCS, | |
// cbResult, | |
// cbResult, | |
// NCRYPT_SILENT_FLAG) = ERROR_SUCCESS then | |
// begin | |
// CertGetStoreProperty(hCS, | |
// CERT_STORE_LOCALIZED_NAME_PROP_ID, | |
// nil, | |
// cbResult); | |
// cbResult := GetLastError; //// CRYPT_E_NOT_FOUND | |
// end; | |
// end; | |
{Enumerate keys in storage provider} | |
ppKeyName := nil; | |
ppEnumState := nil; | |
while NCryptEnumKeys(hProvider, | |
nil, | |
ppKeyName, | |
ppEnumState, | |
NCRYPT_SILENT_FLAG) = ERROR_SUCCESS do | |
begin | |
Memo1.Lines.Add('szName = ' + ppKeyName.pszName + sLineBreak + | |
'szAlgid = ' + ppKeyName.pszAlgid + sLineBreak + | |
'dwLegacyKeySpec = ' + ppKeyName.dwLegacyKeySpec.ToString + sLineBreak + | |
'dwFlags = ' + ppKeyName.dwFlags.ToString); | |
if NCryptOpenKey(hProvider, | |
hKey, | |
ppKeyName.pszName, | |
ppKeyName.dwLegacyKeySpec, | |
0) = ERROR_SUCCESS then | |
try | |
if NCryptGetProperty(hKey, | |
NCRYPT_CERTIFICATE_PROPERTY, | |
nil, | |
0, | |
cbResult, | |
NCRYPT_SILENT_FLAG) = ERROR_SUCCESS then | |
begin | |
SetLength(aOutput, cbResult); | |
if NCryptGetProperty(hKey, | |
NCRYPT_CERTIFICATE_PROPERTY, | |
PByte(aOutput), | |
cbResult, | |
cbResult, | |
NCRYPT_SILENT_FLAG) = ERROR_SUCCESS then | |
begin | |
ppCertContext := CertCreateCertificateContext(X509_ASN_ENCODING, | |
PByte(aOutput), | |
cbResult); | |
if ppCertContext <> nil then | |
try | |
{1st attempt: Decode whole Subject into CERT_NAME_VALUE structure} | |
if CryptDecodeObjectEx(X509_ASN_ENCODING, | |
X509_NAME_VALUE, | |
ppCertContext.pCertInfo.Subject.pbData, | |
ppCertContext.pCertInfo.Subject.cbData, | |
0, | |
nil, | |
nil, | |
cbResult) then | |
begin | |
SetLength(aOutput2, cbResult); | |
if CryptDecodeObjectEx(X509_ASN_ENCODING, | |
X509_NAME_VALUE, | |
ppCertContext.pCertInfo.Subject.pbData, | |
ppCertContext.pCertInfo.Subject.cbData, | |
0, | |
nil, | |
Pointer(aOutput2), | |
cbResult) then | |
begin | |
pCNV := PCertNameValue(aOutput2); | |
Memo1.Lines.Add('X509_NAME_VALUE = ' + PAnsiChar(pCNV.Value.pbData)); | |
end; | |
end; | |
{2nd attempt: Get whole Subject in string format} | |
cbResult := CertNameToStr(X509_ASN_ENCODING, | |
@ppCertContext.pCertInfo.Subject, | |
CERT_OID_NAME_STR, | |
nil, | |
0); | |
SetLength(aOutput2, cbResult * 2); | |
CertNameToStr(X509_ASN_ENCODING, | |
@ppCertContext.pCertInfo.Subject, | |
CERT_OID_NAME_STR, | |
PChar(aOutput2), | |
cbResult); | |
Memo1.Lines.Add('CERT_OID_NAME_STR = ' + WideStringOf(aOutput2)); | |
{3rd attempt: Get Subject Common Name (my goal!)} | |
cbResult := CertGetNameString(ppCertContext, | |
CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
0, | |
nil, | |
nil, | |
0); | |
SetLength(aOutput2, cbResult * 2); | |
CertGetNameString(ppCertContext, | |
CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
0, | |
nil, | |
Pointer(aOutput2), | |
cbResult); | |
Memo1.Lines.Add('CERT_NAME_SIMPLE_DISPLAY_TYPE = ' + WideStringOf(aOutput2)); | |
finally | |
CertFreeCertificateContext(ppCertContext); | |
end; | |
end; | |
end; | |
finally | |
NCryptFreeObject(hKey); | |
end; | |
end; | |
finally | |
NCryptFreeBuffer(ppKeyName); | |
NCryptFreeBuffer(ppEnumState); | |
NCryptFreeObject(hProvider); | |
end; | |
end; | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My goal was to get the CN (common name) from the Subject field of the certificates stored on a Smart Card.
I've used CNG to:
From here on I've used the Crypto API (not CNG) - this part seems to be still current, no deprecation remarks... and seemingly no equivalent CNG APIs to achieve the same results:
(I've kept the other two attempts just for future reference)