如何调用默认的Windows屏幕来选择证书?

时间:2017-05-17 18:07:24

标签: windows delphi winapi certificate x509certificate

THTTPRIO组件,在HTTPWebNode属性中,当您单击ClientCertificate时,Delphi打开一个表单来选择证书并在组件的属性中加载它的信息。这是一个Windows屏幕吗?如果是,我该如何使用它?今天我使用SecureBlackBox在组合框中加载证书,但我想知道是否可以使用此屏幕。 谢谢

Screen to choose certificate

更新

我能够使用ms函数CryptUIDlgSelectCertificateFromStore使用JWAPI显示对话框。现在我遇到了函数结果PCCERT_CONTEXT结构的问题。

var
  P: Pointer;
  Context: PCCERT_CONTEXT;
  Issuer: DATA_BLOB;

  function GetDataBlobText(Data: DATA_BLOB): string;
  begin
    SetString(Result, PAnsiChar(Data.pbData), Data.cbData div SizeOf(AnsiChar));
  end;

begin
  P := CertOpenSystemStore(0, 'MY');
  Context := CryptUIDlgSelectCertificateFromStore(P, 0, PChar('test'), nil, CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
  if Context <> nil then
  begin
    Issuer := Context.pCertInfo.Issuer;
    ShowMessage((GetDataBlobText(Issuer)));
  end;
end;

ShowMessage的结果是:

UPDATE2

谢谢@RbMm。 获取ASN编码字段的字符串(Issuer和Subject)

var
  P: Pointer;
  Context: PCCERT_CONTEXT;
  Subject: DATA_BLOB;
  SubjectStr: string;
  size : Cardinal;
begin
  P := CertOpenSystemStore(0, PAnsiChar('MY'));
  Context := CryptUIDlgSelectCertificateFromStore(P, 0, 'test', 'select certificate',
    CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
  if Context <> nil then
  begin
    Subject := Context.pCertInfo.Subject;
    size := CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, @Subject, CERT_X500_NAME_STR, 0, 0);
    SetString(SubjectStr, PAnsiChar(Subject.pbData), size);
    CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, @Subject, CERT_X500_NAME_STR, PAnsiChar(SubjectStr), size);
    Result := SubjectStr;
  end;

获取原始数据块的字符串(SerialNumber):

var
  SerialNumber: CRYPT_INTEGER_BLOB;
  size : Cardinal;
  s: PWideChar;
  ss: string;
begin
SerialNumber := Context.pCertInfo.SerialNumber;
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, nil, size);
s := AllocMem(SizeOf(Char) * size);
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, s, size);
ss := s;
showmessage(ss);
FreeMem(s, SizeOf(Char) * size);

1 个答案:

答案 0 :(得分:1)

证书中的所有数据blob都已编码。所以你需要解码它。通常使用CryptDecodeObjectEx api。但是对于Issuer(即CERT_NAME_BLOB)解码,您也可以使用CertNameToStrW。只有在将CERT_NAME_BLOB结构中的编码名称转换为以空字符结尾的字符串后,才能将其打印出来。 c / c ++ 上的代码示例:

void PrintIssuer(PCCERT_CONTEXT Context)
{
    CERT_NAME_BLOB Issuer = Context->pCertInfo->Issuer;

    // option #1
    if (ULONG len = CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, 0, 0))
    {
        PWSTR sz = (PWSTR)alloca( len * sizeof(WCHAR));

        if (CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, sz, len))
        {
            DbgPrint("%S\n", sz);
        }
    }

    // option #2 
    PCERT_NAME_INFO pcni;
    ULONG size;

    if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_NAME, Issuer.pbData, Issuer.cbData,
        CRYPT_DECODE_ALLOC_FLAG, 0, &pcni, &size))
    {
        if (DWORD cRDN = pcni->cRDN)
        {
            PCERT_RDN rgRDN = pcni->rgRDN;
            do 
            {
                if (DWORD cRDNAttr = rgRDN->cRDNAttr)
                {
                    PCERT_RDN_ATTR rgRDNAttr = rgRDN->rgRDNAttr;
                    do 
                    {
                        DbgPrint("ObjId = %s\n", rgRDNAttr->pszObjId);

                        switch (rgRDNAttr->dwValueType)
                        {
                        case CERT_RDN_PRINTABLE_STRING:
                            DbgPrint("Value = %s\n", rgRDNAttr->Value.pbData);
                            break;
                        }

                    } while (rgRDNAttr++, --cRDNAttr);
                }

            } while (rgRDN++, --cRDN);
        }

        LocalFree(pcni);
    }
}

和输出

CN=***
ObjId = 2.5.4.3
Value = ***

CN = Value = 之后的字符串相同)

您可以注意到&#34; 2.5.4.3&#34; szOID_COMMON_NAME&#34; CN&#34; 。所以第一个api在发行者名称之前追加 CN = 。第二个变体按原样返回您的名称,并附加ObjId = 2.5.4.3

SerialNumber 转换为可打印的字符串可以采用下一种方式:

CRYPT_INTEGER_BLOB SerialNumber = Context->pCertInfo->SerialNumber;
DWORD cb = 0;
if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, 0, &cb))
{
    PWSTR sz = (PWSTR)alloca( cb * sizeof(WCHAR));
    if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, sz, &cb))
    {
        DbgPrint("%S\n", sz);
    }
}