Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

312 lines
6.9 KiB

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
Abstract:
History:
--*/
#include "precomp.h"
#include <ntsecapi.h>
#include <wbemcli.h>
#include "msgsig.h"
CSignMessage::CSignMessage() : m_bSign(FALSE), CUnk(NULL) { }
CSignMessage::CSignMessage( HCRYPTKEY hKey, HCRYPTHASH hProv )
: m_hKey( hKey ), m_hProv( hProv ), m_bSign( TRUE ), CUnk(NULL)
{
}
CSignMessage::~CSignMessage()
{
if ( !m_bSign )
{
return;
}
CryptDestroyKey( m_hKey );
CryptReleaseContext( m_hProv, 0 );
}
HRESULT CSignMessage::Sign( BYTE* pMsg, DWORD cMsg, BYTE* pSig, DWORD& rcSig )
{
BOOL bRes;
HCRYPTHASH hHash;
if ( !m_bSign )
{
rcSig = 0;
return S_OK;
}
HMAC_INFO hmac;
ZeroMemory( &hmac, sizeof(HMAC_INFO) );
hmac.HashAlgid = CALG_MD5;
bRes = CryptCreateHash( m_hProv, CALG_HMAC, m_hKey, 0, &hHash );
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
bRes = CryptSetHashParam( hHash, HP_HMAC_INFO, LPBYTE(&hmac), 0 );
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
bRes = CryptHashData( hHash, pMsg, cMsg, 0 );
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
bRes = CryptGetHashParam( hHash, HP_HASHVAL, pSig, &rcSig, 0 );
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
CryptDestroyHash( hHash );
return S_OK;
}
HRESULT CSignMessage::Verify( BYTE* pMsg, DWORD cMsg, BYTE* pSig, DWORD cSig )
{
HRESULT hr;
if ( !m_bSign )
{
return cSig == 0 ? S_OK : S_FALSE;
}
BYTE achCheckSig[256];
DWORD cCheckSig = 256;
hr = Sign( pMsg, cMsg, achCheckSig, cCheckSig );
if ( FAILED(hr) )
{
return hr;
}
if ( cSig != cCheckSig )
{
return S_FALSE;
}
return memcmp( achCheckSig, pSig, cSig ) == 0 ? S_OK : S_FALSE;
}
#define PRIV_DATA_SZ 256
HRESULT CSignMessage::Create( LPCWSTR wszName, CSignMessage** ppSignMsg )
{
BOOL bRes;
HCRYPTHASH hHash;
HCRYPTPROV hProv;
HCRYPTKEY hKey;
*ppSignMsg = NULL;
//
// First get OS version. If win9x, no signing will actually be done.
// Only thing keeping from doing win9x is we need a place to securely
// store private data. For NT, we use an LSA secret.
//
OSVERSIONINFO os;
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
bRes = GetVersionEx(&os);
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( os.dwPlatformId != VER_PLATFORM_WIN32_NT )
{
*ppSignMsg = new CSignMessage();
if ( *ppSignMsg == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
(*ppSignMsg)->AddRef();
return S_OK;
}
//
// Now obtain (or create) the secret data to derive private signing key
//
bRes = CryptAcquireContext( &hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT );
if ( !bRes )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// need to create a hash object. This will be used for deriving
// keys for encryption/decryption. The hash object will be initialized
// with the 'secret' random bytes.
//
bRes = CryptCreateHash( hProv, CALG_MD5, 0, 0, &hHash );
if ( !bRes )
{
CryptReleaseContext( hProv, 0 );
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// Open lsa policy. This is where we store our 'secret' bytes.
//
NTSTATUS stat;
PLSA_UNICODE_STRING pPrivateData;
LSA_OBJECT_ATTRIBUTES ObjectAttr;
LSA_HANDLE hPolicy;
ZeroMemory( &ObjectAttr, sizeof(LSA_OBJECT_ATTRIBUTES) );
stat = LsaOpenPolicy( NULL, &ObjectAttr, POLICY_ALL_ACCESS, &hPolicy );
if ( !NT_SUCCESS(stat) )
{
CryptDestroyHash( hHash );
CryptReleaseContext( hProv, 0 );
return stat;
}
//
// now need to get Private Data, which is just stored random bytes.
//
LSA_UNICODE_STRING PrivateKeyName;
PrivateKeyName.Length = wcslen(wszName)*2;
PrivateKeyName.MaximumLength = PrivateKeyName.Length + 2;
PrivateKeyName.Buffer = (WCHAR*)wszName;
stat = LsaRetrievePrivateData( hPolicy,
&PrivateKeyName,
&pPrivateData );
if ( NT_SUCCESS(stat) )
{
//
// we've obtained the private data, fill the hash obj with it.
//
bRes = CryptHashData( hHash,
PBYTE(pPrivateData->Buffer),
pPrivateData->Length,
0 );
if ( !bRes )
{
stat = GetLastError();
}
ZeroMemory( PBYTE(pPrivateData->Buffer), pPrivateData->Length );
LsaFreeMemory( pPrivateData );
}
else if ( stat == STATUS_OBJECT_NAME_NOT_FOUND )
{
BYTE achPrivateData[PRIV_DATA_SZ];
//
// generate the private data and fill the hash obj with it.
//
if ( CryptGenRandom( hProv, PRIV_DATA_SZ, achPrivateData ) )
{
if ( CryptHashData( hHash, achPrivateData, PRIV_DATA_SZ, 0 ) )
{
//
// now store the randomly generated bytes as private data ...
//
LSA_UNICODE_STRING PrivateData;
PrivateData.Length = PRIV_DATA_SZ;
PrivateData.MaximumLength = PRIV_DATA_SZ;
PrivateData.Buffer = (WCHAR*)achPrivateData;
stat = LsaStorePrivateData( hPolicy,
&PrivateKeyName,
&PrivateData );
ZeroMemory( achPrivateData, sizeof(achPrivateData) );
}
else
{
stat = GetLastError();
}
}
else
{
stat = GetLastError();
}
}
LsaClose( hPolicy );
if ( !NT_SUCCESS(stat) )
{
CryptDestroyHash( hHash );
CryptReleaseContext( hProv, 0 );
return HRESULT_FROM_WIN32( stat );
}
//
// now derive the key from the hash object. for MAC, key must use
// RC2 with a CBC mode (default for RC2).
//
bRes = CryptDeriveKey( hProv, CALG_RC2, hHash, 0, &hKey );
CryptDestroyHash( hHash );
if ( !bRes )
{
CryptReleaseContext( hProv, 0 );
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// we have successfully derived our private signing key.
//
*ppSignMsg = new CSignMessage( hKey, hProv );
if ( *ppSignMsg == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
(*ppSignMsg)->AddRef();
return S_OK;
}