// implements much of the exported CKey

#include "stdafx.h"
#include "resource.h"
#include "KeyObjs.h"
#include "passdlg.h"
#include "AdmnInfo.h"

#include "NKChseCA.h"
#include "NKDN.h"
#include "NKDN2.h"
#include "NKKyInfo.h"
#include "NKUsrInf.h"
#include "creating.h"

#include "resource.h"
#include "intrlkey.h"

#include <iis64.h>

#define SECURITY_WIN32
extern "C"
{
    #include <wincrypt.h>
//  #include <sslsp.h>
    #include <schnlsp.h>
    #include <sspi.h>
    #include <ISSPERR.h>
}

#include "mismtchd.h"

#define CRLF "\r\n"
// NON_LOCALIZABLE strings for use in the request header
#define HEADER_ADMINISTRATOR    _T(CRLF "Webmaster: ")
#define HEADER_PHONE            _T(CRLF "Phone: ")
#define HEADER_SERVER           _T(CRLF "Server: ")
#define HEADER_COMMON_NAME      _T(CRLF CRLF "Common-name: ")
#define HEADER_ORG_UNIT         _T(CRLF "Organization Unit: ")
#define HEADER_ORGANIZATION     _T(CRLF "Organization: ")
#define HEADER_LOCALITY         _T(CRLF "Locality: ")
#define HEADER_STATE            _T(CRLF "State: ")
#define HEADER_COUNTRY          _T(CRLF "Country: ")
#define HEADER_END_SPACING      _T(CRLF CRLF )

// defines taken from the old KeyGen utility
#define MESSAGE_HEADER  "-----BEGIN NEW CERTIFICATE REQUEST-----\r\n"
#define MESSAGE_TRAILER "-----END NEW CERTIFICATE REQUEST-----\r\n"
#define MIME_TYPE       "Content-Type: application/x-pkcs10\r\n"
#define MIME_ENCODING   "Content-Transfer-Encoding: base64\r\n\r\n"

int HTUU_encode(unsigned char *bufin, unsigned int nbytes, char *bufcoded);


#define     BACKUP_ID   'KRBK'


IMPLEMENT_DYNCREATE(CKey, CTreeItem);

//------------------------------------------------------------------------------
CKey::CKey():
        m_cbPrivateKey( 0 ),
        m_pPrivateKey( NULL ),
        m_cbCertificate( 0 ),
        m_pCertificate( NULL ),
        m_cbCertificateRequest( 0 ),
        m_pCertificateRequest( NULL )
    {;}

//------------------------------------------------------------------------------
CKey::~CKey()
    {
    LPTSTR pBuff;

    // specifically write zeros out over the password
    try
        {
        pBuff = m_szPassword.GetBuffer(256);
        }
    catch( CException e )
        {
        pBuff = NULL;
        }

    if ( pBuff )
        {
        // zero out the buffer
        ZeroMemory( pBuff, 256 );
        // release the buffer
        m_szPassword.ReleaseBuffer(0);
        }

    // zero out the private key and the certificate
    if ( m_pPrivateKey )
        {
        // zero out the buffer
        ZeroMemory( m_pPrivateKey, m_cbPrivateKey );
        // free the pointer
        GlobalFree( (HANDLE)m_pPrivateKey );
        m_pPrivateKey = NULL;
        }
    if ( m_pCertificate )
        {
        // zero out the buffer
        ZeroMemory( m_pCertificate, m_cbCertificate );
        // free the pointer
        GlobalFree( (HANDLE)m_pCertificate );
        m_pCertificate = NULL;
        }
    if ( m_pCertificateRequest )
        {
        // zero out the buffer
        ZeroMemory( m_pCertificateRequest, m_cbCertificateRequest );
        // free the pointer
        GlobalFree( (HANDLE)m_pCertificateRequest );
        m_pCertificate = NULL;
        }
    }

//------------------------------------------------------------------------------
CKey* CKey::PClone( void )
    {
    CKey*   pClone = NULL;

    // TRY to make a new key object
    try
        {
        pClone = new CKey();
        // copy over all the data
        pClone->CopyDataFrom( this );
        }
    catch( CException e )
        {
        // if the object had been made, delete it
        if ( pClone )
            delete pClone;
        return NULL;
        }

    return pClone;
    }

//------------------------------------------------------------------------------
void CKey::CopyDataFrom( CKey* pKey )
    {
    // make sure this is ok
    ASSERT( pKey );
    ASSERT( pKey->IsKindOf(RUNTIME_CLASS(CKey)) );
    if ( !pKey ) return;

    // delete any data currently in this key
    // zero out the private key and the certificate
    if ( m_pPrivateKey )
        {
        ZeroMemory( m_pPrivateKey, m_cbPrivateKey );
        GlobalFree( (HANDLE)m_pPrivateKey );
        m_pPrivateKey = NULL;
        }
    if ( m_pCertificate )
        {
        ZeroMemory( m_pCertificate, m_cbCertificate );
        GlobalFree( (HANDLE)m_pCertificate );
        m_pCertificate = NULL;
        }
    if ( m_pCertificateRequest )
        {
        ZeroMemory( m_pCertificateRequest, m_cbCertificateRequest );
        GlobalFree( (HANDLE)m_pCertificateRequest );
        m_pCertificate = NULL;
        }

    // copy over the basic stuff
    m_szItemName = pKey->m_szItemName;
    m_iImage = pKey->m_iImage;
    m_szPassword = pKey->m_szPassword;
    m_cbPrivateKey = pKey->m_cbPrivateKey;
    m_cbCertificate = pKey->m_cbCertificate;
    m_cbCertificateRequest = pKey->m_cbCertificateRequest;

    // now the pointer based data
    if ( pKey->m_pPrivateKey )
        {
        m_pPrivateKey = GlobalAlloc( GPTR, m_cbPrivateKey );
        if ( !m_pPrivateKey ) AfxThrowMemoryException();
        memcpy( m_pPrivateKey, pKey->m_pPrivateKey, m_cbPrivateKey );
        }

    if ( pKey->m_pCertificate )
        {
        m_pCertificate = GlobalAlloc( GPTR, m_cbCertificate );
        if ( !m_pCertificate ) AfxThrowMemoryException();
        memcpy( m_pCertificate, pKey->m_pCertificate, m_cbCertificate );
        }

    if ( pKey->m_pCertificateRequest )
        {
        m_pCertificateRequest = GlobalAlloc( GPTR, m_cbCertificateRequest );
        if ( !m_pCertificateRequest ) AfxThrowMemoryException();
        memcpy( m_pCertificateRequest, pKey->m_pCertificateRequest,
                m_cbCertificateRequest );
        }
    }

//------------------------------------------------------------------------------
void CKey::SetName( CString &szNewName )
    {
    m_szItemName = szNewName;
    UpdateCaption();
    SetDirty( TRUE );
    }

//------------------------------------------------------------------------------
void CKey::UpdateIcon( void )
    {
    // if there is no certificate, then the immature key
    if ( !m_pCertificate )
        {
        m_iImage = TREE_ICON_KEY_IMMATURE;
        FSetImage( m_iImage );
        return;
        }

    // there is a certificate, but we need to see if it has
    // expired or not. We do that by cracking the certificate
    // default the key to being ok
    m_iImage = TREE_ICON_KEY_OK;
    CKeyCrackedData cracker;

    // crack the key
    if ( cracker.CrackKey(this) )
        {
        // get the expiration time
        CTime   ctimeExpires( cracker.GetValidUntil() );

        // get the current time
        CTime   ctimeCurrent = CTime::GetCurrentTime();

        // test if it has expired first
        if ( ctimeCurrent > ctimeExpires )
            m_iImage = TREE_ICON_KEY_EXPIRED;
        }

    // set the image
    FSetImage( m_iImage );
    }

//------------------------------------------------------------------------------
BOOL CKey::FInstallCertificate( CString szPath, CString szPass )
    {
    CFile       cfile;
    PVOID       pData = NULL;
    BOOL        fSuccess =FALSE;;

    // open the file
    if ( !cfile.Open( szPath, CFile::modeRead | CFile::shareDenyNone ) )
        return FALSE;

    // how big is the file - add one so we can zero terminate the buffer
    DWORD   cbCertificate = cfile.GetLength() + 1;

    // make sure the file has some size
    if ( !cbCertificate )
        {
        AfxMessageBox( IDS_ERR_INVALID_CERTIFICATE, MB_OK | MB_ICONINFORMATION );
        return FALSE;
        }

    // put the rest of the operation in a try/catch
    // specifically write zeros out over the password
    try
        {
        // allocate space for the data
        pData = GlobalAlloc( GPTR, cbCertificate );
        if ( !pData ) AfxThrowMemoryException();

        // copy in the data from the file to the pointer - will throw and exception
        DWORD cbRead = cfile.Read( pData, cbCertificate );

        // zero terminate for decoding
        ((BYTE*)pData)[cbRead] = 0;

        // great. The last thing left is to uudecode the data we got
        uudecode_cert( (char*)pData, &cbCertificate );

        // close the file
        cfile.Close();

        // install the certificate
        fSuccess = FInstallCertificate( pData, cbCertificate, szPass );
        }
    catch( CException e )
        {
        // if the pointer was allocated, deallocate it
        if ( pData )
            {
            GlobalFree( pData );
            pData = NULL;
            }
        // return failure
        return FALSE;
        }

    // return success
    return fSuccess;
    }

//------------------------------------------------------------------------------
// it is up to the module to verify the certificate
BOOL CKey::FInstallCertificate( PVOID pCert, DWORD cbCert, CString &szPass )
    {
    // cache the old certificate in case the new one fails
    DWORD   old_cbCertificate = m_cbCertificate;
    PVOID   old_pCertificate = m_pCertificate;

    // set the new one into place
    m_cbCertificate = cbCert;
    m_pCertificate = pCert;

/*  // MOVED TO THE MODULE
    // verify the password
    if ( !FVerifyValidPassword(szPass) )
        {
        // resore the old values
        m_cbCertificate = old_cbCertificate;
        m_pCertificate = old_pCertificate;

        // dispose of the new stuff
        GlobalFree( pCert );

        // return false
        return FALSE;
        }
*/

    // Re-Set the password, incase we stored a request from Backup file.
    m_szPassword = szPass;

    // it checks out ok, so we can get rid of the old stuff
    // be careful not to get rid of the current test cert if that is whats there
    if ( old_pCertificate && (old_pCertificate != pCert) )
        {
        GlobalFree( old_pCertificate );
        old_pCertificate = NULL;
        }

    // mark the key as dirty
    SetDirty( TRUE );
    return TRUE;
    }

//------------------------------------------------------------------------------
// verfying a valid password is now a several-stop process. First we have to verify
// that the certificate is the correct one that was requested. Apparenly lots of
// users out there are attempting to install either invalid certificates, or valid
// certificates on the wrong key. Then, when they can't figure out that they messed
// it up, they call us. Or Verisign. To do this we use CAPI2 to get a certificate context
// from the certificate. If that fails is in an invalid cert (say... an EXE file).
// Then we test that certificat's public key to see if it matches the public key
// stored in the request (which we also have to use CAPI2 to crack) If there is no
// request (keyset files) then tough bannanas. They will have to rely on the errors
// returned by the AcquireCredentialsHandle routine.

BOOL CKey::FVerifyValidPassword( CString szPassword )
    {
    PVOID                       pRequestObject = NULL;
    PCCERT_CONTEXT              pcontextCertificate = NULL;
    BOOL                        fAnswer = TRUE;

    SSL_CREDENTIAL_CERTIFICATE  creds;
    CredHandle                  hCreds;
    SECURITY_STATUS             scRet;
    TimeStamp                   ts;
    DWORD                       err;

    CString                     sz;
    CString                     szErr;

    // skip the private, internal request header
    PUCHAR      pRequest = NULL;
    DWORD       cbRequest = 0;

    PUCHAR      pcert = (PUCHAR)m_pCertificate;
    DWORD       cbcert = m_cbCertificate;

    // if the request is there, get its context
    if ( m_pCertificateRequest )
        {
        // see if this request has a header block in front of it. If it does, then use
        // it to get the real request. If it doesn't then just use the request.
        // if it does have a header the first DWORD will be REQUEST_HEADER_IDENTIFIER
        if ( *(DWORD*)m_pCertificateRequest == REQUEST_HEADER_IDENTIFIER )
            {
            // get the real request that comes after the request header
            pRequest = (PUCHAR)m_pCertificateRequest +
                        ((LPREQUEST_HEADER)m_pCertificateRequest)->cbSizeOfHeader;
            cbRequest = ((LPREQUEST_HEADER)m_pCertificateRequest)->cbRequestSize;
            }
        else
            {
            // there is no header
            pRequest = (PUCHAR)m_pCertificateRequest;
            cbRequest = m_cbCertificateRequest;
            }

        // note that if the request isn't there or if it is invalid it is still
        // ok to try and verify the certificate
        DWORD   cbBuffSize = 0;
        // start by decoding the request object - get the size of the required buffer
        CryptDecodeObject( X509_ASN_ENCODING,
                                X509_CERT_REQUEST_TO_BE_SIGNED,
                                pRequest,
                                cbRequest,
                                0,
                                NULL,
                                &cbBuffSize );

        // now make an attempt to decode it
        pRequestObject = GlobalAlloc( GPTR, cbBuffSize );
        if ( !pRequestObject )
            goto GetCredentials;
        if ( !CryptDecodeObject( X509_ASN_ENCODING,
                                X509_CERT_REQUEST_TO_BE_SIGNED,
                                pRequest,
                                cbRequest,
                                0,
                                pRequestObject,
                                &cbBuffSize ) )
            {
            GlobalFree( pRequestObject );
            pRequestObject = NULL;
            goto GetCredentials;
            }

        }

  GetCredentials:

    // OK. Verisign and Certsrv and probably others too wrap the certificate in a wrapper
    // that, while following the normal formatting convensions, causes the context routine
    // below to fail. This means that we have to detect if the wrapper is there and, if it
    // is, we need to skip it. Fortunately, the wrapper contains the word "certificate"
    // yes, this is sorta grungy, but its what petesk said to do.
    sz = pcert;
    sz = sz.Left(20);
    if ( sz.Find("certificate") == 6 )
        {
        pcert += 17;
        }

    // get the context on the certificate
    pcontextCertificate = CertCreateCertificateContext( X509_ASN_ENCODING, pcert, cbcert );
    // if we were unable to create the context, then fail with an appropriate error message
    if ( !pcontextCertificate )
        {
        // get the error for the dialog
        err = GetLastError();

        // tell the user that they chose an invalid certificate
        sz.LoadString( IDS_CERTERR_INVALID_CERTIFICATE );
        sz.Format( "%s%x", sz, err );
        AfxMessageBox( sz );

        // time to leave with a failure
        fAnswer = FALSE;
        goto cleanup;
        }


    // if we have both a cert context AND a decoded request object, then we can confirm that
    // they are in fact matched to each other.
    if ( pcontextCertificate && pRequestObject )
        {
        // use CAPI2 to do the comparison between the two public key info structures
        if ( !CertComparePublicKeyInfo( X509_ASN_ENCODING,
                            &((PCERT_REQUEST_INFO)pRequestObject)->SubjectPublicKeyInfo,
                            &pcontextCertificate->pCertInfo->SubjectPublicKeyInfo ) )
            {
            // The certificate is mis-matched to the request. Fail with the right message
            CMismatchedCertDlg  mismatchdlg(
                        &((PCERT_REQUEST_INFO)pRequestObject)->Subject,
                        &pcontextCertificate->pCertInfo->Subject);
            mismatchdlg.DoModal();

            fAnswer = FALSE;
            goto cleanup;
            }
        }


    // use SSL to finish verifying the password
    // fill in the credentials record
    creds.cbPrivateKey = m_cbPrivateKey;
    creds.pPrivateKey = (PUCHAR)m_pPrivateKey;
    creds.cbCertificate = m_cbCertificate;
    creds.pCertificate = (PUCHAR)m_pCertificate;

    // prepare the password
    creds.pszPassword = (PSTR)LPCTSTR(szPassword);

    // attempt to get the credentials
    scRet = AcquireCredentialsHandleW(  NULL,                   // My name (ignored)
                                        UNISP_NAME_W,           // Package
                                        SECPKG_CRED_INBOUND,    // Use
                                        NULL,                   // Logon ID (ignored)
                                        &creds,                 // auth data
                                        NULL,                   // dce-stuff
                                        NULL,                   // dce-stuff
                                        &hCreds,                // handle
                                        &ts );                  // we really get it below

    // check the results
    if ( FAILED(scRet) )
        {
        sz.Empty();

        // add on the appropriate sub-error message
        switch( scRet )
            {
            case SEC_E_UNKNOWN_CREDENTIALS:
            case SEC_E_NO_CREDENTIALS:
            case SEC_E_INTERNAL_ERROR:
                // Unfortunately, SChannel returns the "internal error" when a bad password
                // is returned. There may or may not be other circumstances under which this
                // error is returned, but this is by far the most common. Also I taked to
                // PeteSk about it and he agreed that this is how it should be used.
                // bad password
                sz.LoadString( IDS_CERTERROR_BADPASSWORD );
                break;
            case SEC_E_SECPKG_NOT_FOUND:
            case SEC_E_BAD_PKGID:
                // the system does not have the package installed
                sz.LoadString( IDS_CERTERROR_PACKAGELOAD_ERROR );
                break;
            case SEC_E_INSUFFICIENT_MEMORY:
                sz.LoadString( IDS_CERTERR_LOMEM );
                break;
            case SEC_E_CANNOT_INSTALL:
                sz.LoadString( EDS_CERTERR_SCHNL_BAD_INIT );
                break;
            default:
                sz.LoadString( IDS_CERTERR_SCHNL_GENERIC );
                break;
            }

        // put up the error message
        szErr.LoadString( IDS_CERTERR_SCHANNEL_ERR );
        sz.Format( "%s%s%x", sz, szErr, scRet );
        AfxMessageBox( sz );

        // return failure
        fAnswer = FALSE;
        goto cleanup;
        }

    // close the credentials handle
    FreeCredentialsHandle( &hCreds );

    // clean up the certificate contexts
cleanup:
    if ( pRequestObject )
        GlobalFree( pRequestObject );
    if ( pcontextCertificate )
        CertFreeCertificateContext( pcontextCertificate );

    // return success
    return fAnswer;
    }

//------------------------------------------------------------------------------
void CKey::OutputHeader( CFile* pFile, PVOID privData1, PVOID privData2 )
    {
    PADMIN_INFO pInfo = (PADMIN_INFO)privData1;

    // we start this by getting the DN strings from either the dialog that was
    // passed in through privData or throught cracking the certificate.
    CString szCN, szOU, szO, szL, szS, szC;
    CKeyCrackedData cracker;
    // the only way to get the info is from cracking an existing cert
    if ( cracker.CrackKey(this) )
        {
        cracker.GetDNNetAddress( szCN );
        cracker.GetDNUnit( szOU );
        cracker.GetDNOrganization( szO );
        cracker.GetDNLocality( szL );
        cracker.GetDNState( szS );
        cracker.GetDNCountry( szC );
        }
    else
        {
        if (pInfo->pCommonName) szCN = *pInfo->pCommonName;
        if (pInfo->pOrgUnit) szOU = *pInfo->pOrgUnit;
        if (pInfo->pOrg) szO = *pInfo->pOrg;
        if (pInfo->pLocality) szL = *pInfo->pLocality;
        if (pInfo->pState) szS = *pInfo->pState;
        if (pInfo->pCountry) szC = *pInfo->pCountry;
        }

    // ok, output all the strings, starting with the administrator information
    CString     sz = HEADER_ADMINISTRATOR;
    pFile->Write( sz, sz.GetLength() );
    if ( pInfo->pEmail )
        pFile->Write( *pInfo->pEmail, pInfo->pEmail->GetLength() );

    sz = HEADER_PHONE;
    pFile->Write( sz, sz.GetLength() );
    if ( pInfo->pPhone )
        pFile->Write( *pInfo->pPhone, pInfo->pPhone->GetLength() );

    sz = HEADER_SERVER;
    pFile->Write( sz, sz.GetLength() );
    sz.LoadString( IDS_SERVER_INFO_STRING );
    pFile->Write( sz, sz.GetLength() );

    sz = HEADER_COMMON_NAME;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szCN, szCN.GetLength() );

    sz = HEADER_ORG_UNIT;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szOU, szOU.GetLength() );

    sz = HEADER_ORGANIZATION;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szO, szO.GetLength() );

    sz = HEADER_LOCALITY;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szL, szL.GetLength() );

    sz = HEADER_STATE;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szS, szS.GetLength() );

    sz = HEADER_COUNTRY;
    pFile->Write( sz, sz.GetLength() );
    pFile->Write( szC, szC.GetLength() );

    sz = HEADER_END_SPACING;
    pFile->Write( sz, sz.GetLength() );
    }

//------------------------------------------------------------------------------
// this routine is based on the routine "Requestify" from KeyGen
BOOL CKey::FOutputRequestFile( CString szFile, BOOL fMime, PVOID privData )
    {
    PADMIN_INFO     pAdminInfo= (PADMIN_INFO)privData;
    ADMIN_INFO      AdminInfo;
    CAdminInfoDlg   aiDlg;

    // ok, the priv data points to a structure that contains three strings
    // describing the admin requesting this request. If it is null, then we
    // must ask this information.
    if ( !pAdminInfo )
        {
        if ( aiDlg.DoModal() != IDOK )
            return FALSE;

        // fill the darned thing in
        ZeroMemory( &AdminInfo, sizeof(AdminInfo) );
        pAdminInfo = &AdminInfo;
        AdminInfo.pEmail = &aiDlg.m_sz_email;
        AdminInfo.pPhone = &aiDlg.m_sz_phone;
        }


    PUCHAR  pEncoded;
    DWORD   cbData = m_cbCertificateRequest;

    // encode the request into a new pointer
    pEncoded = PCreateEncodedRequest( m_pCertificateRequest, &cbData, FALSE );

/*
    PUCHAR  pb;
    DWORD   cb;
    PUCHAR  p;
    DWORD   Size;

    PUCHAR  pSource;
    DWORD   cbSource;

    ASSERT( pSource );
    ASSERT( cbSource );

    // we don't actually want to change the source data, so make a copy of it first
    pSource = (PUCHAR)GlobalAlloc( GPTR, m_cbCertificateRequest );
    if ( !pSource )
        {
        AfxThrowMemoryException();
        return FALSE;
        }
    cbSource = m_cbCertificateRequest;
    // copy over the data
    CopyMemory( pSource, m_pCertificateRequest, cbSource );

    cb = (cbSource * 3 / 4) + 1024;

    pb = (PUCHAR)LocalAlloc( LMEM_FIXED, cb );

    if ( !pb )
        return FALSE;

    p = pb;

    if ( fMime )
        {
        Size = strlen( MIME_TYPE );
        CopyMemory( p, MIME_TYPE, Size );
        p += Size;

        Size = strlen( MIME_ENCODING );
        CopyMemory( p, MIME_ENCODING, Size );
        p += Size;
        }
    else
        {
        Size = strlen( MESSAGE_HEADER );
        CopyMemory( p, MESSAGE_HEADER, Size );
        p += Size;
        }

    do
        {
        Size = HTUU_encode( pSource,
                        (cbSource > 48 ? 48 : cbSource),
                        (PCHAR)p );
        p += Size;

        *p++ = '\r';
        *p++ = '\n';

        if ( cbSource < 48 )
            break;

        cbSource -= 48;
        pSource += 48;
        } while (cbSource);

    if ( !fMime )
        {
        Size = strlen( MESSAGE_TRAILER );
        CopyMemory( p, MESSAGE_TRAILER, Size );
        p += Size;
        }
*/

    // write the requestified data into the target file
    try
        {
        // try to open the file
        CFile   cfile;
        if ( !cfile.Open(szFile, CFile::modeCreate | CFile::modeWrite) )
            {
            AfxMessageBox( IDS_IO_ERROR );
            return FALSE;
            }

        // write out the header stuff that simon at Verisign
        // requested at the LAST POSSIBLE MINUTE!!!
        OutputHeader( &cfile, pAdminInfo, NULL );

        // write the data to the file
//      cfile.Write( pb, (p - pb) );
        cfile.Write( pEncoded, cbData );

        // close the file
        cfile.Close();
        }
    catch( CException e )
        {
        if ( pEncoded )
            LocalFree( pEncoded );
        return FALSE;
        }

    if ( pEncoded )
        LocalFree( pEncoded );
    return TRUE;
    }

//------------------------------------------------------------------------
PUCHAR  PCreateEncodedRequest( PVOID pRequest, DWORD* pcb, BOOL fMime )
    {
    PUCHAR  pb;
    DWORD   cb;
    PUCHAR  p;
    DWORD   Size;

    PUCHAR  pSource;
    DWORD   cbSource;

    DWORD   cbRequest = *pcb;

    ASSERT( pcb && *pcb );
    ASSERT( pRequest );

    // get the correct pointer to and size of the request, taking into account the header
    LPREQUEST_HEADER pHeader = (LPREQUEST_HEADER)pRequest;
    if ( pHeader->Identifier == REQUEST_HEADER_IDENTIFIER )
        {
        pRequest = (PUCHAR)pRequest + pHeader->cbSizeOfHeader;
        cbRequest = *pcb = pHeader->cbRequestSize;
        }

    // we don't actually want to change the source data, so make a copy of it first
    pSource = (PUCHAR)GlobalAlloc( GPTR, cbRequest );
    if ( !pSource )
        {
        AfxThrowMemoryException();
        return FALSE;
        }
    cbSource = cbRequest;
    // copy over the data
    CopyMemory( pSource, pRequest, cbSource );

    cb = (cbSource * 3 / 4) + 1024;

    pb = (PUCHAR)LocalAlloc( LMEM_FIXED, cb );

    if ( !pb )
        return FALSE;

    p = pb;

    if ( fMime )
        {
        Size = strlen( MIME_TYPE );
        CopyMemory( p, MIME_TYPE, Size );
        p += Size;

        Size = strlen( MIME_ENCODING );
        CopyMemory( p, MIME_ENCODING, Size );
        p += Size;
        }
    else
        {
        Size = strlen( MESSAGE_HEADER );
        CopyMemory( p, MESSAGE_HEADER, Size );
        p += Size;
        }

    do
        {
        Size = HTUU_encode( pSource,
                        (cbSource > 48 ? 48 : cbSource),
                        (PCHAR)p );
        p += Size;

        *p++ = '\r';
        *p++ = '\n';

        if ( cbSource < 48 )
            break;

        cbSource -= 48;
        pSource += 48;
        } while (cbSource);

    if ( !fMime )
        {
        Size = strlen( MESSAGE_TRAILER );
        CopyMemory( p, MESSAGE_TRAILER, Size );
        p += Size;
        }

    // set the count of bytes into place
    *pcb = DIFF(p - pb);

    //return the pointer to the encoded information
    return pb;
    }


// Taken right out of KenGen.c
static char six2pr[64] =
{
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};

// Taken right out of KenGen.c
/*--- function HTUU_encode -----------------------------------------------
 *
 *   Encode a single line of binary data to a standard format that
 *   uses only printing ASCII characters (but takes up 33% more bytes).
 *
 *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
 *                      a multiple of three, then the byte just beyond
 *                      the last byte in the buffer must be 0.
 *             nbytes   is the number of bytes in that buffer.
 *                      This cannot be more than 48.
 *             bufcoded points to an output buffer.  Be sure that this
 *                      can hold at least 1 + (4*nbytes)/3 characters.
 *
 *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
 *                      contain printing ASCII characters representing
 *                      those binary bytes. This may include one or
 *                      two '=' characters used as padding at the end.
 *                      The last byte is a zero byte.
 *             Returns the number of ASCII characters in "bufcoded".
 */

// Now the HTUU_encode is taken from infocomm/common/fcache and has been fixed by amallet
// it has been modified slightly to take into account that the output buffer was resized above
int HTUU_encode(unsigned char *bufin, unsigned int nbytes, char *bufcoded)
{
   register char *outptr = bufcoded;
   unsigned int i;
   BOOL fOneByteDiff = FALSE;
   BOOL fTwoByteDiff = FALSE;
   unsigned int iRemainder = 0;
   unsigned int iClosestMultOfThree = 0;

   iRemainder = nbytes % 3; //also works for nbytes == 1, 2
   fOneByteDiff = (iRemainder == 1 ? TRUE : FALSE);
   fTwoByteDiff = (iRemainder == 2 ? TRUE : FALSE);
   iClosestMultOfThree = ((nbytes - iRemainder)/3) * 3 ;

   //
   // Encode bytes in buffer up to multiple of 3 that is closest to nbytes.
   //
   for (i=0; i< iClosestMultOfThree ; i += 3) {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
      *(outptr++) = six2pr[bufin[2] & 077];         /* c4 */

      bufin += 3;
   }

   //
   // We deal with trailing bytes by pretending that the input buffer has been padded with
   // zeros. Expressions are thus the same as above, but the second half drops off b'cos
   // ( a | ( b & 0) ) = ( a | 0 ) = a
   //
   if (fOneByteDiff)
   {
       *(outptr++) = six2pr[*bufin >> 2]; /* c1 */
       *(outptr++) = six2pr[((*bufin << 4) & 060)]; /* c2 */

       //pad with '='
       *(outptr++) = '='; /* c3 */
       *(outptr++) = '='; /* c4 */
   }
   else if (fTwoByteDiff)
   {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074)];/*c3*/

      //pad with '='
       *(outptr++) = '='; /* c4 */
   }

   //encoded buffer must be zero-terminated
   *outptr = '\0';

   return DIFF(outptr - bufcoded);
}


//============================ BASED ON SETKEY
const int pr2six[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

//
//  We have to squirt a record into the decoded stream
//

#define CERT_RECORD            13
#define CERT_SIZE_HIBYTE        2       //  Index into record of record size
#define CERT_SIZE_LOBYTE        3

unsigned char abCertHeader[] = {0x30, 0x82,           // Record
                                0x00, 0x00,           // Size of cert + buff
                                0x04, 0x0b, 0x63, 0x65,// Cert record data
                                0x72, 0x74, 0x69, 0x66,
                                0x69, 0x63, 0x61, 0x74,
                                0x65 };

void uudecode_cert(char *bufcoded, DWORD *pcbDecoded )
{
    int nbytesdecoded;
    char *bufin = bufcoded;
    unsigned char *bufout = (unsigned char *)bufcoded;
    unsigned char *pbuf;
    int nprbytes;
    char * beginbuf = bufcoded;

    ASSERT(bufcoded);
    ASSERT(pcbDecoded);

    /* Strip leading whitespace. */

    while(*bufcoded==' ' ||
          *bufcoded == '\t' ||
          *bufcoded == '\r' ||
          *bufcoded == '\n' )
    {
          bufcoded++;
    }

    //
    //  If there is a beginning '---- ....' then skip the first line
    //

    if ( bufcoded[0] == '-' && bufcoded[1] == '-' )
    {
        bufin = strchr( bufcoded, '\n' );

        if ( bufin )
        {
            bufin++;
            bufcoded = bufin;
        }
        else
        {
            bufin = bufcoded;
        }
    }
    else
    {
        bufin = bufcoded;
    }

    //
    //  Strip all cr/lf from the block
    //

    pbuf = (unsigned char *)bufin;
    while ( *pbuf )
    {
        if ( *pbuf == '\r' || *pbuf == '\n' )
        {
            memmove( (void*)pbuf, pbuf+1, strlen( (char*)pbuf + 1) + 1 );
        }
        else
        {
            pbuf++;
        }
    }

    /* Figure out how many characters are in the input buffer.
     * If this would decode into more bytes than would fit into
     * the output buffer, adjust the number of input bytes downwards.
     */

    while(pr2six[*(bufin++)] <= 63);
    nprbytes = DIFF(bufin - bufcoded) - 1;
    nbytesdecoded = ((nprbytes+3)/4) * 3;

    bufin  = bufcoded;

    while (nprbytes > 0) {
        *(bufout++) =
            (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    if(nprbytes & 03) {
        if(pr2six[bufin[-2]] > 63)
            nbytesdecoded -= 2;
        else
            nbytesdecoded -= 1;
    }

    //
    //  Now we need to add a new wrapper sequence around the certificate
    //  indicating this is a certificate
    //

    memmove( beginbuf + sizeof(abCertHeader),
             beginbuf,
             nbytesdecoded );

    memcpy( beginbuf,
            abCertHeader,
            sizeof(abCertHeader) );

    //
    //  The beginning record size is the total number of bytes decoded plus
    //  the number of bytes in the certificate header
    //

    beginbuf[CERT_SIZE_HIBYTE] = (BYTE) (((USHORT)nbytesdecoded+CERT_RECORD) >> 8);
    beginbuf[CERT_SIZE_LOBYTE] = (BYTE) ((USHORT)nbytesdecoded+CERT_RECORD);

    nbytesdecoded += sizeof(abCertHeader);

    if ( pcbDecoded )
        *pcbDecoded = nbytesdecoded;
}
// ============ END BASED ON SETKEY



//------------------------------------------------------------------------------
BOOL CKey::FImportKeySetFiles( CString szPrivate, CString szPublic, CString &szPass )
    {
    BOOL fSuccess = TRUE;

    // in this routine, we load the data from the file, initialize it, and ask
    // the user for a password, which we then confirm with AcquireCredHandle.

    // several things we will be doing can throw, so use a try/catch
    try
        {
        // start by opening the private data file
        CFile   cfile( szPrivate, CFile::modeRead|CFile::shareDenyWrite );
        // get the length of the file
        m_cbPrivateKey = cfile.GetLength();
        // create a handle to hold the data
        m_pPrivateKey = GlobalAlloc( GPTR, m_cbPrivateKey );
        if ( !m_pPrivateKey )
            {
            cfile.Close();
            AfxThrowMemoryException();
            };
        // great, now read the data out of the file
        cfile.Read( m_pPrivateKey, m_cbPrivateKey );
        // close the file
        cfile.Close();


        // reading in the certificate is easy because that was done elsewhere
        if ( szPublic && !szPublic.IsEmpty() )
            {
            fSuccess = FInstallCertificate( szPublic, szPass );
            if ( fSuccess )
                // set the password
                m_szPassword = szPass;
            }
        }
    catch( CException e )
        {
        return FALSE;
        }

    return fSuccess;
    }

//------------------------------------------------------------------------------
void ReadWriteDWORD( CFile *pFile, DWORD *pDword, BOOL fRead );
void ReadWriteString( CFile *pFile, CString &sz, BOOL fRead );
void ReadWriteBlob( CFile *pFile, PVOID pBlob, DWORD cbBlob, BOOL fRead );
//------------------------------------------------------------------------------
BOOL CKey::FImportExportBackupFile( CString szFile, BOOL fImport )
    {
    DWORD   dword;
    UINT nOpenFlags;
    CConfirmPassDlg     dlgconfirm;


    // set up the right open flags
    if ( fImport )
        nOpenFlags = CFile::modeRead | CFile::shareDenyNone;
    else
        nOpenFlags = CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive;

    // put it in a try/catch to get any errors
    try
        {
        CFile   file( szFile, nOpenFlags );

        // do the backup id
        dword = BACKUP_ID;
        ReadWriteDWORD( &file, &dword, fImport );

        // check the backup id
        if ( dword != BACKUP_ID )
            {
            AfxMessageBox( IDS_KEY_FILE_INVALID );
            return FALSE;
            }


        // start with the name of the key
        CString szName = GetName();
        ReadWriteString( &file, szName, fImport );
        if ( fImport ) SetName( szName );


        // now the private key data size
        ReadWriteDWORD( &file, &m_cbPrivateKey, fImport );

        // make a private key data pointer if necessary
        if ( fImport && m_cbPrivateKey )
            {
            m_pPrivateKey = GlobalAlloc( GPTR, m_cbPrivateKey );
            if ( !m_pPrivateKey ) AfxThrowMemoryException();
            }

        // use the private key pointer
        if ( m_cbPrivateKey )
            ReadWriteBlob( &file, m_pPrivateKey, m_cbPrivateKey, fImport );


        // now the certificate
        ReadWriteDWORD( &file, &m_cbCertificate, fImport );

        // make a data pointer if necessary
        if ( fImport && m_cbCertificate )
            {
            m_pCertificate = GlobalAlloc( GPTR, m_cbCertificate );
            if ( !m_pCertificate ) AfxThrowMemoryException();
            }

        // use the private key pointer
        if ( m_cbCertificate )
            ReadWriteBlob( &file, m_pCertificate, m_cbCertificate, fImport );


        // now the request
        ReadWriteDWORD( &file, &m_cbCertificateRequest, fImport );

        // make a data pointer if necessary
        if ( fImport && m_cbCertificateRequest )
            {
            m_pCertificateRequest = GlobalAlloc( GPTR, m_cbCertificateRequest );
            if ( !m_pCertificateRequest ) AfxThrowMemoryException();
            }

        // use the private key pointer
        if ( m_cbCertificateRequest )
            ReadWriteBlob( &file, m_pCertificateRequest, m_cbCertificateRequest, fImport );


        // finally, if we are importing, we need to confirm the password
        // Except if there is no Cert, which means Import of a Request
        if ( m_cbCertificate && fImport )
            {
            //if we are importing, get the password first
            if ( dlgconfirm.DoModal() != IDOK )
                return FALSE;

            if ( !FVerifyValidPassword(dlgconfirm.m_szPassword) )
                {
                // the Verify Valid Password routine puts up all
                // the correct error dialogs now
                return FALSE;
                }

            // set the password into place
            m_szPassword = dlgconfirm.m_szPassword;
            }

        }
    catch( CException e )
        {
        // return failure
        return FALSE;
        }

    // return success
    return TRUE;
    }



// file utilities
//---------------------------------------------------------------------------
void ReadWriteDWORD( CFile *pFile, DWORD *pDword, BOOL fRead )
    {
    ASSERT(pFile);
    ASSERT(pDword);

    // read it or write it
    if ( fRead )
        pFile->Read( (void*)pDword, sizeof(DWORD) );
    else
        pFile->Write( (void*)pDword, sizeof(DWORD) );
    }

//---------------------------------------------------------------------------
void ReadWriteString( CFile *pFile, CString &sz, BOOL fRead )
    {
    ASSERT(pFile);
    ASSERT(sz);

    // get the length of the string
    DWORD   cbLength = sz.GetLength();
    ReadWriteDWORD(pFile,&cbLength,fRead );

    // read or write the string
    LPTSTR psz = sz.GetBuffer( cbLength+1 );
    ReadWriteBlob(pFile, psz, cbLength+1, fRead);

    // free the string buffer
    sz.ReleaseBuffer();
    }

//---------------------------------------------------------------------------
void ReadWriteBlob( CFile *pFile, PVOID pBlob, DWORD cbBlob, BOOL fRead )
    {
    ASSERT(pFile);
    ASSERT(pBlob);
    ASSERT(cbBlob);

    // read it or write it
    if ( fRead )
        pFile->Read( pBlob, cbBlob );
    else
        pFile->Write( pBlob, cbBlob );
    }