2017-01-26 20 views
0

안녕하세요 저는 사용자 이름/암호를 자격 증명으로 사용할 때 제대로 작동하는 사용자 지정 자격 증명 제공자를 작성했습니다. 암호는 블루투스를 통해 전송됩니다. 설명서가 구현할 인터페이스를 알려주는만큼 어렵지 않았습니다.KERB_CERTIFICATE_LOGON에 연결하는 KSP 작성 방법

이제 자격 증명을 변경하여 인증서를 사용하려고합니다. 나는 이것을 위해 KERB_CERTIFICATE_LOGON 구조를 사용해야한다는 것을 알았다. 더 깊은 주제로 들어가면 this article by microsoft에 설명 된대로 사용자 지정 키 저장소 공급자를 구현해야한다는 것을 알았습니다. 그리고 그것이 내가 잃어버린 지점입니다. 아마 바른 문서를 찾기 위해 어리석은 일이지만, KERB_CERTIFICATE_LOGON 구조체의 CspData 필드에서 참조 할 수있는 KSP를 구현하기 위해 구현해야하는 인터페이스를 찾을 수 없습니다. 난 그냥 NCRYPT_CERTIFICATE_PROPERTY (위의 링크 된 기사에서 언급)에 대한 방법과 잔뜩을 찾아 놀라운 Google에서 두 결과를 공개했다.

credentialprovider와 KSP를 연결할 때 도움이되는 this SO-question을 찾았지만 KSP를 작성하는 방법을 설명하지 않습니다. :-(

사람이 어디에서 정보를 찾거나 유사한 시나리오 (단지 메소드 선언, 어떻게 KERB_CERTIFICATE_LOGON에 대한 호출에서 결과 KSP를 사용하는 방법)에 사용되는 KSP의 간단한 예제를 표시하는 나를 인도 할 수 있습니까?

답변

2

Microsoft Cryptographic Provider Development Kit는 KSP의 좋은 샘플을 가지고

이 샘플에 추가해야 할 유일한 것은 SmartCardKeyCertificate 재산 이 코드는이 같은 것 구현이다..

SECURITY_STATUS 
WINAPI 
KSPGetKeyProperty(
__in NCRYPT_PROV_HANDLE hProvider, 
__in NCRYPT_KEY_HANDLE hKey, 
__in LPCWSTR pszProperty, 
__out_bcount_part_opt(cbOutput, *pcbResult) PBYTE pbOutput, 
__in DWORD cbOutput, 
__out DWORD * pcbResult, 
__in DWORD dwFlags) 
{ 

    ... 

    if (wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0) 
    { 
     dwProperty = SAMPLEKSP_CERTIFICATE_PROPERTY; 
     cbResult = GetCertificateSize(); //the size of the certificate that is associated with the key 
    } 

    *pcbResult = cbResult; 
    if(pbOutput == NULL) 
    { 
     Status = ERROR_SUCCESS; 
     goto cleanup; 
    } 

    if(cbOutput < *pcbResult) 
    { 
     Status = NTE_BUFFER_TOO_SMALL; 
     goto cleanup; 
    } 

    if (wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0) 
    { 
     CopyMemory(pbOutput, crt, sizeof(crt)); 
    } 


    switch(dwProperty) 
    { 
    case SAMPLEKSP_CERTIFICATE_PROPERTY: 
     CopyMemory(pbOutput, GetCertificate(), cbResult); //Copy to pbOutput the certificate in binary form 
     break; 

    ... 

    } 

    ... 

} 

당신이 KSP를 구현하고 등록 후, CredentialProvider가 상호 작용할 수 있습니다 :

  1. , pKerbCspInfo->의 KeySpec KSP와 상호 작용 :이 코드에서 두 가지 일이있다

    HRESULT MyCredential::GetSerialization(
        CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, 
        CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 
        PWSTR* ppwszOptionalStatusText, 
        CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon 
        ) 
    { 
    
        ... 
    
        ULONG ulAuthPackage; 
        HRESULT hr = RetrieveNegotiateAuthPackage(&ulAuthPackage); 
        ConstructAuthInfo(&pcpcs->rgbSerialization, &pcpcs->cbSerialization); 
    
        if (SUCCEEDED(hr)) 
        { 
         pcpcs->ulAuthenticationPackage = ulAuthPackage; 
         pcpcs->clsidCredentialProvider = CLSID_MyCredentialProvider; 
    
         // At this point the credential has created the serialized credential used for logon 
         // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know 
         // that we have all the information we need and it should attempt to submit the 
         // serialized credential. 
         *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED; 
        } 
    
        return hr; 
    } 
    
    HRESULT RetrieveNegotiateAuthPackage(ULONG * pulAuthPackage) 
    { 
        HRESULT hr = S_OK; 
        HANDLE hLsa = NULL; 
    
        NTSTATUS status = LsaConnectUntrusted(&hLsa); 
        if (SUCCEEDED(HRESULT_FROM_NT(status))) 
        { 
    
         ULONG ulAuthPackage; 
         LSA_STRING lsaszKerberosName; 
         LsaInitString(&lsaszKerberosName, MICROSOFT_KERBEROS_NAME_A); 
         status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage); 
         if (SUCCEEDED(HRESULT_FROM_NT(status))) 
         { 
          *pulAuthPackage = ulAuthPackage; 
          hr = S_OK; 
         } 
         else 
         { 
          hr = HRESULT_FROM_NT(status); 
         } 
         LsaDeregisterLogonProcess(hLsa); 
        } 
        else 
        { 
         hr = HRESULT_FROM_NT(status); 
        } 
    
        return hr; 
    } 
    
    void ConstructAuthInfo(LPBYTE* ppbAuthInfo, ULONG *pulAuthInfoLen) 
    { 
        WCHAR szCardName[] = L""; // no card name specified but you can put one if you want 
        WCHAR szContainerName[] = L"my_key_name"; 
        WCHAR szReaderName[] = L""; 
        WCHAR szCspName[] = L"My Key Storage Provider"; 
        WCHAR szPin[] = L"11111111"; 
        ULONG ulPinByteLen = wcslen(szPin) * sizeof(WCHAR); 
        WCHAR szUserName[] = L"user"; 
        ULONG ulUserByteLen = wcslen(szUserName) * sizeof(WCHAR); 
        WCHAR szDomainName[] = L"testdomain.com"; 
        ULONG ulDomainByteLen = wcslen(szDomainName) * sizeof(WCHAR); 
        LPBYTE pbAuthInfo = NULL; 
        ULONG ulAuthInfoLen = 0; 
        KERB_CERTIFICATE_LOGON *pKerbCertLogon; 
        KERB_SMARTCARD_CSP_INFO *pKerbCspInfo; 
        LPBYTE pbDomainBuffer, pbUserBuffer, pbPinBuffer; 
        LPBYTE pbCspData; 
        LPBYTE pbCspDataContent; 
    
        ULONG ulCspDataLen = sizeof(KERB_SMARTCARD_CSP_INFO)-sizeof(TCHAR)+ 
         (wcslen(szCardName) + 1) * sizeof(WCHAR)+ 
         (wcslen(szCspName) + 1) * sizeof(WCHAR)+ 
         (wcslen(szContainerName) + 1) * sizeof(WCHAR)+ 
         (wcslen(szReaderName) + 1) * sizeof(WCHAR); 
    
        ulAuthInfoLen = sizeof(KERB_CERTIFICATE_LOGON)+ 
         ulDomainByteLen + sizeof(WCHAR)+ 
         ulUserByteLen + sizeof(WCHAR)+ 
         ulPinByteLen + sizeof(WCHAR)+ 
         ulCspDataLen; 
    
        pbAuthInfo = (LPBYTE)CoTaskMemAlloc(ulAuthInfoLen); 
        ZeroMemory(pbAuthInfo, ulAuthInfoLen); 
    
        pbDomainBuffer = pbAuthInfo + sizeof(KERB_CERTIFICATE_LOGON); 
        pbUserBuffer = pbDomainBuffer + ulDomainByteLen + sizeof(WCHAR); 
        pbPinBuffer = pbUserBuffer + ulUserByteLen + sizeof(WCHAR); 
        pbCspData = pbPinBuffer + ulPinByteLen + sizeof(WCHAR); 
    
        memcpy(pbDomainBuffer, szDomainName, ulDomainByteLen); 
        memcpy(pbUserBuffer, szUserName, ulUserByteLen); 
        memcpy(pbPinBuffer, szPin, ulPinByteLen); 
    
        pKerbCertLogon = (KERB_CERTIFICATE_LOGON*)pbAuthInfo; 
    
        pKerbCertLogon->MessageType = KerbCertificateLogon; 
        pKerbCertLogon->DomainName.Length = (USHORT)ulDomainByteLen; 
        pKerbCertLogon->DomainName.MaximumLength = (USHORT)(ulDomainByteLen + sizeof(WCHAR)); 
        pKerbCertLogon->DomainName.Buffer = (PWSTR)(pbDomainBuffer-pbAuthInfo); 
        pKerbCertLogon->UserName.Length = (USHORT)ulUserByteLen; 
        pKerbCertLogon->UserName.MaximumLength = (USHORT)(ulUserByteLen + sizeof(WCHAR)); 
        pKerbCertLogon->UserName.Buffer = (PWSTR)(pbUserBuffer-pbAuthInfo); 
        pKerbCertLogon->Pin.Length = (USHORT)ulPinByteLen; 
        pKerbCertLogon->Pin.MaximumLength = (USHORT)(ulPinByteLen + sizeof(WCHAR)); 
        pKerbCertLogon->Pin.Buffer = (PWSTR)(pbPinBuffer-pbAuthInfo); 
    
        pKerbCertLogon->CspDataLength = ulCspDataLen; 
        pKerbCertLogon->CspData = (PUCHAR)(pbCspData-pbAuthInfo); 
    
        pKerbCspInfo = (KERB_SMARTCARD_CSP_INFO*)pbCspData; 
        pKerbCspInfo->dwCspInfoLen = ulCspDataLen; 
        pKerbCspInfo->MessageType = 1; 
        pKerbCspInfo->KeySpec = CERT_NCRYPT_KEY_SPEC; 
    
        pKerbCspInfo->nCardNameOffset = 0; 
        pKerbCspInfo->nReaderNameOffset = pKerbCspInfo->nCardNameOffset + wcslen(szCardName) + 1; 
        pKerbCspInfo->nContainerNameOffset = pKerbCspInfo->nReaderNameOffset + wcslen(szReaderName) + 1; 
        pKerbCspInfo->nCSPNameOffset = pKerbCspInfo->nContainerNameOffset + wcslen(szContainerName) + 1; 
    
        pbCspDataContent = pbCspData + sizeof(KERB_SMARTCARD_CSP_INFO)-sizeof(TCHAR); 
        memcpy(pbCspDataContent + (pKerbCspInfo->nCardNameOffset * sizeof(WCHAR)), szCardName, wcslen(szCardName) * sizeof(WCHAR)); 
        memcpy(pbCspDataContent + (pKerbCspInfo->nReaderNameOffset * sizeof(WCHAR)), szReaderName, wcslen(szReaderName) * sizeof(WCHAR)); 
        memcpy(pbCspDataContent + (pKerbCspInfo->nContainerNameOffset * sizeof(WCHAR)), szContainerName, wcslen(szContainerName) * sizeof(WCHAR)); 
        memcpy(pbCspDataContent + (pKerbCspInfo->nCSPNameOffset * sizeof(WCHAR)), szCspName, wcslen(szCspName) * sizeof(WCHAR)); 
    
        *ppbAuthInfo = pbAuthInfo; 
        *pulAuthInfoLen = ulAuthInfoLen; 
    } 
    

    CERT_NCRYPT_KEY_SPEC 여야합니다. this 않음에 따라 적절한 구조 직렬화

  2. :

    pKerbCertLogon-> DomainName.Buffer = (PWSTR) (pbDomainBuffer-pbAuthInfo); pKerbCertLogon-> UserName.Buffer = (PWSTR) (pbUserBuffer-pbAuthInfo); pKerbCertLogon-> Pin.Buffer = (PWSTR) (pbPinBuffer-pbAuthInfo); pKerbCertLogon-> CspData = (PUCHAR) (pbCspData-pbAuthInfo);

또한 도메인 컨트롤러는 "Kerberos 인증"템플릿으로 인증서를 발급해야합니다.

+0

고맙습니다. 그동안 나는 CNG-Kit에서 샘플을 마침내 발견했습니다. 그러나 나는 그것을 작동시키지 못했습니다. 당신이 권고 한 것처럼 KeySpec을 CERT_NCRYPT_KEY_SPEC로 바꾸는 것은 그 속임수를했습니다. – Frank

+0

한 가지 더 질문 : 공급자를 C++로 이식 할 수 있습니까? 나는 그것을 시도했고 KSP의 등록이 효과가있다. 그러나 그것은 C++ 버전이 로그온 프로세스에서 호출되지 않는 것처럼 보입니다. GetKeyStorageInterface의 내보내기와 관련이 있다고 생각하지만 작동하도록 변경해야한다는 단서가 없습니다. – Frank

+0

@Frank 나는 C 또는 C++의 사용에 차이가 있다고 생각하지 않는다.정확히 작동하지 않는 것은 무엇입니까? 다른 시나리오에서 C++ 공급자 작업을합니까? 예를 들면 다음과 같습니다. NCryptOpenStorageProvider (...); NCryptOpenKey (...); – plstryagain