/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: CheckSC Abstract: This application is used to provide a snapshot of the Calais (Smart Card Resource Manager) service's status, and to display certificates on smart cards via the common WinNT UI. CheckSC -- describes the RM status and displays each available sc cert(s) -r Readername -- for just one reader -sig -- display signature key certs only -ex -- display exchange key certs only -nocert -- don't look for certs to display -key -- verify keyset public key matches cert public key Author: Amanda Matlosz (AMatlosz) 07/14/1998 Environment: Win32 Console App Notes: For use in NT5 public key rollout testing --*/ /*++ need to include the following libs: calaislb.lib (unicode build: calaislbw.lib) winscard.lib --*/ #include #include #include // #include // #include #include #include #include #include #include #ifndef SCARD_PROVIDER_CSP #define SCARD_PROVIDER_CSP 2 #endif #define KERB_PKINIT_CLIENT_CERT_TYPE szOID_PKIX_KP_CLIENT_AUTH // // Globals // int g_nKeys; DWORD g_rgKeySet[2]; // { AT_KEYEXCHANGE , AT_SIGNATURE }; SCARDCONTEXT g_hSCardCtx; LPTSTR g_szReaderName; DWORD g_dwNumReaders; BOOL g_fReaderNameAllocd; BOOL g_fChain = FALSE; BOOL g_fPublicKeyCheck = FALSE; SCARD_READERSTATE* g_pReaderStatusArray; const char* g_szEx = TEXT("exchange"); const char* g_szSig = TEXT("signature"); // // Functions // /////////////////////////////////////////////////////////////////////////////// // DisplayUsage does easy UI void DisplayUsage() { cout << "\n" << "CheckSC [-sig|-ex|-nocert|-chain|-key] [-r \"Readername\"]\n" << " -sig Displays only signature key certificates.\n" << " -ex Displays only signature key certificates.\n" << " -nocert Does not display smart card certificates.\n" << " -chain Check trust status.\n" << " -key Verify keyset public key matches certificate public key.\n" << endl; } /////////////////////////////////////////////////////////////////////////////// // ProcessCommandLine does the dirty work, sets behavior globals bool ProcessCommandLine(DWORD cArgs, LPCTSTR rgszArgs[]) { // set everything to default g_szReaderName = NULL; // no reader g_rgKeySet[0] = AT_KEYEXCHANGE; // certs for both kinds of keys g_rgKeySet[1] = AT_SIGNATURE; g_nKeys = 2; if (cArgs == 1) { return true; } // For each arg, verify that it's a real arg and deal with it bool fLookForReader = false; bool fCertOptionSpecified = false; bool fBogus = FALSE; for (DWORD n=1; nszReader = (LPCTSTR)g_szReaderName; g_pReaderStatusArray->dwCurrentState = SCARD_STATE_UNAWARE; } // ...And get the reader status from the resource manager lReturn = SCardGetStatusChange(g_hSCardCtx, INFINITE, // hardly g_pReaderStatusArray, g_dwNumReaders); if (SCARD_S_SUCCESS != lReturn) { TCHAR szMsg[128]; // %Xx sprintf(szMsg, "SCardGetStatusChange failed with: 0x%X.\n", lReturn); cout << szMsg << endl; sprintf(szMsg, "MStringCount returned %d readers.\n", g_dwNumReaders); cout << szMsg << endl; return; } // Finally, display all reader information DWORD dwState = 0; for (DWORD dwRdrSt = 0; dwRdrSt < g_dwNumReaders; dwRdrSt++) { //--- reader: readerName\n cout << TEXT("--- reader: ") << g_pReaderStatusArray[dwRdrSt].szReader << TEXT("\n"); //--- status: /bits/\n bool fOr = false; cout << TEXT("--- status: "); dwState = g_pReaderStatusArray[dwRdrSt].dwEventState; if (0 != (dwState & SCARD_STATE_UNKNOWN)) { cout << TEXT("SCARD_STATE_UNKNOWN "); fOr = true; } if (0 != (dwState & SCARD_STATE_UNAVAILABLE)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_UNAVAILABLE "); fOr = true; } if (0 != (dwState & SCARD_STATE_EMPTY)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_EMPTY "); fOr = true; } if (0 != (dwState & SCARD_STATE_PRESENT)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_PRESENT ";) fOr = true; } if (0 != (dwState & SCARD_STATE_EXCLUSIVE)) { if (fOr) { cout << TEXT("| ";) } cout << TEXT("SCARD_STATE_EXCLUSIVE "); fOr = true; } if (0 != (dwState & SCARD_STATE_INUSE)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_INUSE "); fOr = true; } if (0 != (dwState & SCARD_STATE_MUTE)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_MUTE "); fOr = true; } if (0 != (dwState & SCARD_STATE_UNPOWERED)) { if (fOr) { cout << TEXT("| "); } cout << TEXT("SCARD_STATE_UNPOWERED"); fOr = true; } cout << TEXT("\n"); //--- status: what scstatus would say\n cout << TEXT("--- status: "); // NO CARD if(dwState & SCARD_STATE_EMPTY) { cout << TEXT("No card.");// SC_STATUS_NO_CARD; } // CARD in reader: SHARED, EXCLUSIVE, FREE, UNKNOWN ? else if(dwState & SCARD_STATE_PRESENT) { if (dwState & SCARD_STATE_MUTE) { cout << TEXT("The card is unrecognized or not responding.");// SC_STATUS_UNKNOWN; } else if (dwState & SCARD_STATE_INUSE) { if(dwState & SCARD_STATE_EXCLUSIVE) { cout << TEXT("Card is in use exclusively by another process.");// SC_STATUS_EXCLUSIVE; } else { cout << TEXT("The card is being shared by a process.");// SC_STATUS_SHARED; } } else { cout << TEXT("The card is available for use.");// SC_SATATUS_AVAILABLE; } } // READER ERROR: at this point, something's gone wrong else // dwState & SCARD_STATE_UNAVAILABLE { cout << TEXT("Card/Reader not responding.");// SC_STATUS_ERROR; } cout << TEXT("\n"); //- card name(s):\n\n cout << TEXT("--- card: "); if (0 < g_pReaderStatusArray[dwRdrSt].cbAtr) { // // Get the name of the card // LPTSTR szCardName = NULL; DWORD dwAutoAllocate = SCARD_AUTOALLOCATE; lReturn = SCardListCards(g_hSCardCtx, g_pReaderStatusArray[dwRdrSt].rgbAtr, NULL, 0, (LPTSTR)&szCardName, &dwAutoAllocate); if (SCARD_S_SUCCESS != lReturn || NULL == szCardName) { cout << TEXT("Unknown Card."); } else { LPCTSTR szName = szCardName; bool fNotFirst = false; for (szName = FirstString(szName); NULL != szName; szName = NextString(szName)) { if (fNotFirst) cout << TEXT(", "); cout << szName; fNotFirst = true; } } if (NULL != szCardName) { SCardFreeMemory(g_hSCardCtx, (PVOID)szCardName); } } cout << TEXT("\n") << endl; } } /////////////////////////////////////////////////////////////////////////////// // GetCertContext -- called by DisplayCerts PCCERT_CONTEXT GetCertContext(HCRYPTPROV* phProv, HCRYPTKEY* phKey, DWORD dwKeySpec) { PCCERT_CONTEXT pCertCtx = NULL; LONG lResult = SCARD_S_SUCCESS; BOOL fSts = FALSE; PCERT_PUBLIC_KEY_INFO pInfo = NULL; CRYPT_KEY_PROV_INFO KeyProvInfo; LPSTR szContainerName = NULL; LPSTR szProvName = NULL; LPWSTR wszContainerName = NULL; LPWSTR wszProvName = NULL; DWORD cbContainerName, cbProvName; LPBYTE pbCert = NULL; DWORD cbCertLen; int nLen = 0; // // Get the cert from this key // fSts = CryptGetKeyParam( *phKey, KP_CERTIFICATE, NULL, &cbCertLen, 0); if (!fSts) { lResult = GetLastError(); if (ERROR_MORE_DATA != lResult) { return NULL; } } lResult = SCARD_S_SUCCESS; pbCert = (LPBYTE)LocalAlloc(LPTR, cbCertLen); if (NULL == pbCert) { return NULL; } fSts = CryptGetKeyParam( *phKey, KP_CERTIFICATE, pbCert, &cbCertLen, 0); if (!fSts) { return NULL; } // // Convert the certificate into a Cert Context. // pCertCtx = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, cbCertLen); if (NULL == pCertCtx) { lResult = GetLastError(); goto ErrorExit; } // // Perform public key check // if (g_fPublicKeyCheck) // -key { cout << "\nPerforming public key matching test...\n"; DWORD dwPCBsize = 0; fSts = CryptExportPublicKeyInfo( *phProv, // in dwKeySpec, // in X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, // in NULL, &dwPCBsize // in, out ); if (!fSts) { lResult = GetLastError(); TCHAR sz[256]; sprintf(sz,"CryptExportPublicKeyInfo failed: 0x%x\n ", lResult); cout << sz; goto ErrorExit; } if (dwPCBsize == 0) { lResult = SCARD_E_UNEXPECTED; // huh? cout << "CryptExportPublicKeyInfo succeeded but returned size==0\n"; goto ErrorExit; } pInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(LPTR, dwPCBsize); if (NULL == pInfo) { lResult = E_OUTOFMEMORY; cout << "Could not complete key test; out of memory.\n"; goto ErrorExit; } fSts = CryptExportPublicKeyInfo( *phProv, dwKeySpec, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, &dwPCBsize ); if (!fSts) { lResult = GetLastError(); TCHAR sz[256]; sprintf(sz,"CryptExportPublicKeyInfo failed: 0x%x\n ", lResult); cout << sz; goto ErrorExit; } fSts = CertComparePublicKeyInfo( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, // from the private keyset &(pCertCtx->pCertInfo->SubjectPublicKeyInfo) // public key from cert ); if (!fSts) { lResult = GetLastError(); goto ErrorExit; } cout << "Public key matching test succeeded.\n"; } // // Associate cryptprovider w/ the private key property of this cert // // ... need the container name fSts = CryptGetProvParam( *phProv, PP_CONTAINER, NULL, // out &cbContainerName, // in/out 0); if (!fSts) { lResult = GetLastError(); goto ErrorExit; } szContainerName = (LPSTR)LocalAlloc(LPTR, cbContainerName); fSts = CryptGetProvParam( *phProv, PP_CONTAINER, (PBYTE)szContainerName, &cbContainerName, 0); if (!fSts) { lResult = GetLastError(); goto ErrorExit; } nLen = MultiByteToWideChar( GetACP(), MB_PRECOMPOSED, szContainerName, -1, NULL, 0); if (0 < nLen) { wszContainerName = (LPWSTR)LocalAlloc(LPTR, nLen*sizeof(WCHAR)); nLen = MultiByteToWideChar( GetACP(), MB_PRECOMPOSED, szContainerName, -1, wszContainerName, nLen); if (0 == nLen) { lResult = GetLastError(); goto ErrorExit; } } // ... need the provider name fSts = CryptGetProvParam( *phProv, PP_NAME, NULL, // out &cbProvName, // in/out 0); if (!fSts) { lResult = GetLastError(); goto ErrorExit; } szProvName = (LPSTR)LocalAlloc(LPTR, cbProvName); fSts = CryptGetProvParam( *phProv, PP_NAME, (PBYTE)szProvName, // out &cbProvName, // in/out 0); if (!fSts) { lResult = GetLastError(); goto ErrorExit; } nLen = MultiByteToWideChar( GetACP(), MB_PRECOMPOSED, szProvName, -1, NULL, 0); if (0 < nLen) { wszProvName = (LPWSTR)LocalAlloc(LPTR, nLen*sizeof(WCHAR)); nLen = MultiByteToWideChar( GetACP(), MB_PRECOMPOSED, szProvName, -1, wszProvName, nLen); if (0 == nLen) { lResult = GetLastError(); goto ErrorExit; } } // // Set the cert context properties to reflect the prov info // KeyProvInfo.pwszContainerName = wszContainerName; KeyProvInfo.pwszProvName = wszProvName; KeyProvInfo.dwProvType = PROV_RSA_FULL; KeyProvInfo.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; KeyProvInfo.cProvParam = 0; KeyProvInfo.rgProvParam = NULL; KeyProvInfo.dwKeySpec = dwKeySpec; fSts = CertSetCertificateContextProperty( pCertCtx, CERT_KEY_PROV_INFO_PROP_ID, 0, (void *)&KeyProvInfo); if (!fSts) { lResult = GetLastError(); // the cert's been incorrectly created -- scrap it. CertFreeCertificateContext(pCertCtx); pCertCtx = NULL; goto ErrorExit; } ErrorExit: if (NULL != pInfo) { LocalFree(pInfo); } if(NULL != szContainerName) { LocalFree(szContainerName); } if(NULL != szProvName) { LocalFree(szProvName); } if(NULL != wszContainerName) { LocalFree(wszContainerName); } if(NULL != wszProvName) { LocalFree(wszProvName); } return pCertCtx; } /*++ DisplayChainInfo: This code verifies that the SC cert is valid. Uses identical code to KDC cert chaining engine. Author: Todds --*/ DWORD DisplayChainInfo(PCCERT_CONTEXT pCert) { BOOL fRet = FALSE; DWORD dwErr = 0; TCHAR sz[256]; CERT_CHAIN_PARA ChainParameters = {0}; LPSTR ClientAuthUsage = KERB_PKINIT_CLIENT_CERT_TYPE; PCCERT_CHAIN_CONTEXT ChainContext = NULL; ChainParameters.cbSize = sizeof(CERT_CHAIN_PARA); ChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; ChainParameters.RequestedUsage.Usage.cUsageIdentifier = 1; ChainParameters.RequestedUsage.Usage.rgpszUsageIdentifier = &ClientAuthUsage; if (!CertGetCertificateChain( HCCE_LOCAL_MACHINE, pCert, NULL, // evaluate at current time NULL, // no additional stores &ChainParameters, CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, // reserved &ChainContext )) { dwErr = GetLastError(); sprintf(sz,"CertGetCertificateChain failed: 0x%x\n ", dwErr); cout << sz; } else { if (ChainContext->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) { dwErr = ChainContext->TrustStatus.dwErrorStatus; sprintf(sz,"CertGetCertificateChain TrustStatus failed, see wincrypt.h: 0x%x\n ", dwErr); cout << sz; } } if (ChainContext != NULL) { CertFreeCertificateChain(ChainContext); } return dwErr; } /////////////////////////////////////////////////////////////////////////////// // DisplayCerts void DisplayCerts() { _ASSERTE(0 < g_nKeys); // For each reader that has a card, load the CSP and display the cert for (DWORD dw = 0; dw < g_dwNumReaders; dw++) { LPTSTR szCardName = NULL; LPTSTR szCSPName = NULL; if(0 >= g_pReaderStatusArray[dw].cbAtr) { // no point to do anymore work in this iteration continue; } // // Inform user of current test // cout << TEXT("\n=======================================================\n") << TEXT("Analyzing card in reader: ") << g_pReaderStatusArray[dw].szReader << TEXT("\n"); // Get the name of the card DWORD dwAutoAllocate = SCARD_AUTOALLOCATE; LONG lReturn = SCardListCards(g_hSCardCtx, g_pReaderStatusArray[dw].rgbAtr, NULL, 0, (LPTSTR)&szCardName, &dwAutoAllocate); if (SCARD_S_SUCCESS == lReturn) { dwAutoAllocate = SCARD_AUTOALLOCATE; lReturn = SCardGetCardTypeProviderName( g_hSCardCtx, szCardName, SCARD_PROVIDER_CSP, (LPTSTR)&szCSPName, &dwAutoAllocate); if (SCARD_S_SUCCESS != lReturn) { TCHAR szErr[16]; sprintf(szErr, "0x%X", lReturn); cout << TEXT("Error on SCardGetCardTypeProviderName for ") << szCardName << TEXT(": ") << szErr << TEXT("\n"); } } // Prepare FullyQualifiedContainerName for CryptAcCntx call TCHAR szFQCN[256]; sprintf(szFQCN, "\\\\.\\%s\\", g_pReaderStatusArray[dw].szReader); HCRYPTPROV hProv = NULL; if (SCARD_S_SUCCESS == lReturn) { BOOL fSts = CryptAcquireContext( &hProv, szFQCN, // default container via reader szCSPName, PROV_RSA_FULL, CRYPT_SILENT); // Enumerate the keys user specified and display the certs... if (fSts) { for (int n=0; n