/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * WinCAPICryptoKeyRSA := RSA Keys
 *
 * Author(s): Berin Lautenbach
 *
 * $Id: WinCAPICryptoKeyRSA.cpp 1819347 2017-12-27 18:05:51Z scantor $
 *
 */

#if defined (XSEC_HAVE_WINCAPI)

#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
#include <xsec/enc/WinCAPI/WinCAPICryptoKeyRSA.hpp>
#include <xsec/enc/XSCrypt/XSCryptCryptoBase64.hpp>
#include <xsec/framework/XSECError.hpp>

#include "../../utils/XSECAlgorithmSupport.hpp"

#include <xercesc/util/Janitor.hpp>

XSEC_USING_XERCES(ArrayJanitor);

#if !defined (CRYPT_OAEP)
#    define CRYPT_OAEP              0x00000040
#    define KP_OAEP_PARAMS          36
#endif

#if !defined (CRYPT_DECRYPT_RSA_NO_PADDING_CHECK)
#   define CRYPT_DECRYPT_RSA_NO_PADDING_CHECK   0x00000020
#endif

WinCAPICryptoKeyRSA::WinCAPICryptoKeyRSA(HCRYPTPROV prov) {

    // Create a new key to be loaded as we go

    m_key = 0;
    m_p = prov;
    m_keySpec = 0;

    mp_exponent = NULL;
    m_exponentLen = 0;
    mp_modulus = NULL;
    m_modulusLen = 0;
};

WinCAPICryptoKeyRSA::~WinCAPICryptoKeyRSA() {

    // If we have a RSA, delete it

    if (m_key != 0)
        CryptDestroyKey(m_key);

    if (mp_exponent)
        delete[] mp_exponent;
    if (mp_modulus)
        delete[] mp_modulus;
};

WinCAPICryptoKeyRSA::WinCAPICryptoKeyRSA(HCRYPTPROV prov, 
                                         HCRYPTKEY k) : m_p(prov) {

    m_key = k;        // NOTE - We OWN this handle
    m_keySpec = 0;

    mp_exponent = mp_modulus = NULL;
    m_exponentLen = m_modulusLen = 0;
}

WinCAPICryptoKeyRSA::WinCAPICryptoKeyRSA(HCRYPTPROV prov, 
                                         DWORD keySpec,
                                         bool isPrivate) : m_p(prov) {

    if (isPrivate == false) {

        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPICryptoKeyRSA - Public keys defined via keySpec ctor not yet supported");


    }

    if (!CryptGetUserKey(prov, keySpec, &m_key)) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA Unable to retrieve user key");
    }

    //m_key = 0;
    m_keySpec = keySpec;

    mp_exponent = mp_modulus = NULL;
    m_exponentLen = m_modulusLen = 0;
}

const XMLCh * WinCAPICryptoKeyRSA::getProviderName() const {
	return DSIGConstants::s_unicodeStrPROVWinCAPI;
}

// Generic key functions

XSECCryptoKey::KeyType WinCAPICryptoKeyRSA::getKeyType() const {

    // Find out what we have
    if (m_key == 0) {

        if (m_keySpec != 0)
            return KEY_RSA_PRIVATE;

        if (mp_exponent == NULL ||
            mp_modulus == NULL)
            return KEY_NONE;
        else
            return KEY_RSA_PUBLIC;

    }

    if (m_keySpec != 0)
        return KEY_RSA_PAIR;

    return KEY_RSA_PUBLIC;
}


// --------------------------------------------------------------------------------
//           Load key from parameters
// --------------------------------------------------------------------------------

void WinCAPICryptoKeyRSA::loadPublicModulusBase64BigNums(const char* b64, unsigned int len) {

    if (mp_modulus != NULL) {
        delete[] mp_modulus;
        mp_modulus = NULL;        // In case we get an exception
    }

    mp_modulus = WinCAPICryptoProvider::b642WinBN(b64, len, m_modulusLen);

}

void WinCAPICryptoKeyRSA::loadPublicExponentBase64BigNums(const char* b64, unsigned int len) {

    if (mp_exponent != NULL) {
        delete[] mp_exponent;
        mp_exponent = NULL;        // In case we get an exception
    }

    mp_exponent = WinCAPICryptoProvider::b642WinBN(b64, len, m_exponentLen);
}

HCRYPTKEY WinCAPICryptoKeyRSA::importKey() const {

    if (m_key != 0 ||
        mp_exponent == NULL ||
        mp_modulus == NULL)

        return m_key;


    // Create a RSA Public-Key blob

    // First build a buffer to hold everything

    BYTE * blobBuffer;
    unsigned int blobBufferLen = WINCAPI_BLOBHEADERLEN + WINCAPI_RSAPUBKEYLEN + m_modulusLen;
    XSECnew(blobBuffer, BYTE[blobBufferLen]);
    ArrayJanitor<BYTE> j_blobBuffer(blobBuffer);

    // Blob header
    BLOBHEADER * header = (BLOBHEADER *) blobBuffer;

    header->bType = PUBLICKEYBLOB;
    header->bVersion = 0x02;            // We are using a version 2 blob
    header->reserved = 0;
    header->aiKeyAlg = CALG_RSA_SIGN;

    // Now the public key header
    RSAPUBKEY * pubkey = (RSAPUBKEY *) (blobBuffer + WINCAPI_BLOBHEADERLEN);

    pubkey->magic = 0x31415352;            // ASCII encoding of RSA1
    pubkey->bitlen = m_modulusLen * 8;        // Number of bits in prime modulus
    pubkey->pubexp = 0;
    BYTE * i = ((BYTE *) &(pubkey->pubexp));
    for (unsigned int j = 0; j < m_exponentLen; ++j)
        *i++ = mp_exponent[j];

    // Now copy in the modulus
    i = (BYTE *) (pubkey);
    i += WINCAPI_RSAPUBKEYLEN;

    memcpy(i, mp_modulus, m_modulusLen);

    // Now that we have the blob, import
    BOOL fResult = CryptImportKey(
                    m_p,
                    blobBuffer,
                    blobBufferLen,
                    0,                // Not signed
                    0,                // No flags
                    &m_key);

    if (fResult == 0) {

        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA Error attempting to import key parameters");

    }

    return m_key;
}


// --------------------------------------------------------------------------------
//           Verify a signature encoded as a Base64 string
// --------------------------------------------------------------------------------

bool WinCAPICryptoKeyRSA::verifySHA1PKCS1Base64Signature(const unsigned char * hashBuf, 
                                 unsigned int hashLen,
                                 const char * base64Signature,
                                 unsigned int sigLen,
								 XSECCryptoHash::HashType type) const {

    // Use the currently loaded key to validate the Base64 encoded signature

    if (m_key == 0) {

        // Try to import from the parameters
        importKey();

        if (m_key == 0) {
            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA - Attempt to validate signature with empty key");
        }
    }

    /* Is this a hash we support? */
    ALG_ID alg;
    switch (type) {
    case (XSECCryptoHash::HASH_MD5):
        alg = CALG_MD5;
        break;
    case (XSECCryptoHash::HASH_SHA1):
        alg=CALG_SHA1;
        break;
    default:
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA Unsupported hash algorithm for RSA sign - only MD5 or SHA1 supported");
    }

    // Decode the signature
    unsigned char * rawSig;
    DWORD rawSigLen;
    XSECnew(rawSig, BYTE [sigLen]);
    ArrayJanitor<BYTE> j_rawSig(rawSig);

    // Decode the signature
    XSCryptCryptoBase64 b64;

    b64.decodeInit();
    rawSigLen = b64.decode((unsigned char *) base64Signature, sigLen, rawSig, sigLen);
    rawSigLen += b64.decodeFinish(&rawSig[rawSigLen], sigLen - rawSigLen);

    BYTE * rawSigFinal;
    XSECnew(rawSigFinal, BYTE[rawSigLen]);
    ArrayJanitor<BYTE> j_rawSigFinal(rawSigFinal);

    BYTE * j, *l;
    j = rawSig;
    l = rawSigFinal + rawSigLen - 1;

    while (l >= rawSigFinal) {
        *l-- = *j++;
    }

    // Have to create a Windows hash object and feed in the hash
    BOOL fResult;
    HCRYPTHASH h;
    fResult = CryptCreateHash(m_p,
                    alg,
                    0,
                    0,
                    &h);

    if (!fResult) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error creating Windows Hash Object");
    }

    // Feed the hash value into the newly created hash object
    fResult = CryptSetHashParam(
                    h,
                    HP_HASHVAL,
                    (unsigned char *) hashBuf,
                    0);

    if (!fResult) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error Setting Hash Value in Windows Hash object");
    }

    // Now validate
    fResult = CryptVerifySignature(
                h,
                rawSigFinal,
                rawSigLen,
                m_key,
                NULL,
                0);

    if (!fResult) {

        DWORD error = GetLastError();

        if (error != NTE_BAD_SIGNATURE) {
            if (h)
                CryptDestroyHash(h);
            throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error occurred in RSA validation");
        }

        if (h)
            CryptDestroyHash(h);
        return false;
    }

    if (h)
        CryptDestroyHash(h);
    return true;
}

// --------------------------------------------------------------------------------
//           Sign and encode result as a Base64 string
// --------------------------------------------------------------------------------


unsigned int WinCAPICryptoKeyRSA::signSHA1PKCS1Base64Signature(unsigned char * hashBuf,
        unsigned int hashLen,
        char * base64SignatureBuf,
        unsigned int base64SignatureBufLen,
		XSECCryptoHash::HashType type) const {

    // Sign a pre-calculated hash using this key

    if (m_keySpec == 0) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Attempt to sign data using a public or un-loaded key");
    }

    /* Is this a hash we support? */
    ALG_ID alg;
    switch (type) {
    case (XSECCryptoHash::HASH_MD5):
        alg = CALG_MD5;
        break;
    case (XSECCryptoHash::HASH_SHA1):
        alg = CALG_SHA1;
        break;
    default:
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA Unsupported hash algorithm for RSA sign - only MD5 or SHA1 supported");
    }

    // Have to create a Windows hash object and feed in the hash
    BOOL fResult;
    HCRYPTHASH h;
    fResult = CryptCreateHash(m_p,
                    alg,
                    0,
                    0,
                    &h);

    if (!fResult) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error creating Windows Hash Object");
    }

    // Feed the hash value into the newly created hash object
    fResult = CryptSetHashParam(
                    h,
                    HP_HASHVAL,
                    hashBuf,
                    0);

    if (!fResult) {
        if (h)
            CryptDestroyHash(h);
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error Setting Hash Value in Windows Hash object");
    }

    // Now sign
    DWORD rawSigLen;
    fResult = CryptSignHash(
                h,
                m_keySpec,
                NULL,
                0,
                NULL,
                &rawSigLen);

    if (!fResult || rawSigLen < 1) {

        if (h)
            CryptDestroyHash(h);
        throw XSECCryptoException(XSECCryptoException::RSAError,
        "WinCAPI:RSA - Error occurred obtaining RSA sig length");
    }

    BYTE * rawSig;
    XSECnew(rawSig, BYTE[rawSigLen]);
    ArrayJanitor<BYTE> j_rawSig(rawSig);

    fResult = CryptSignHash(
                h,
                m_keySpec,
                NULL,
                0,
                rawSig,
                &rawSigLen);

    if (!fResult || rawSigLen < 1) {

        // Free the hash
        if (h)
            CryptDestroyHash(h);

        throw XSECCryptoException(XSECCryptoException::RSAError,
        "WinCAPI:RSA - Error occurred signing hash");
    }

    // Free the hash
    if (h)
        CryptDestroyHash(h);

    // Now encode into a signature block
    BYTE *rawSigFinal;
    XSECnew(rawSigFinal, BYTE[rawSigLen]);
    ArrayJanitor<BYTE> j_rawSigFinal(rawSigFinal);

    BYTE * i, * j;

    i = rawSig;
    j = rawSigFinal + rawSigLen - 1;

    while (j >= rawSigFinal) {
        *j-- = *i++;
    }

    // Now encode
    XSCryptCryptoBase64 b64;
    b64.encodeInit();
    unsigned int ret = b64.encode(rawSigFinal, rawSigLen, (unsigned char *) base64SignatureBuf, base64SignatureBufLen);
    ret += b64.encodeFinish((unsigned char *) &base64SignatureBuf[ret], base64SignatureBufLen - ret);

    return ret;
}

XSECCryptoKey * WinCAPICryptoKeyRSA::clone() const {

    WinCAPICryptoKeyRSA * ret;

    XSECnew(ret, WinCAPICryptoKeyRSA(m_p));

    if (m_key != 0) {

        // CryptDuplicateKey is not supported in Windows NT, so we need to export and then
        // reimport the key to get a copy

        BYTE keyBuf[2048];
        DWORD keyBufLen = 2048;
        CryptExportKey(m_key, 0, PUBLICKEYBLOB, 0, keyBuf, &keyBufLen);

        // Now re-import
        CryptImportKey(m_p, keyBuf, keyBufLen, NULL, 0, &ret->m_key);
    }

    ret->m_keySpec = m_keySpec;

    ret->m_exponentLen = m_exponentLen;
    if (mp_exponent != NULL) {
        XSECnew(ret->mp_exponent, BYTE[m_exponentLen]);
        memcpy(ret->mp_exponent, mp_exponent, m_exponentLen);
    }
    else
        ret->mp_exponent = NULL;

    ret->m_modulusLen = m_modulusLen;
    if (mp_modulus != NULL) {
        XSECnew(ret->mp_modulus, BYTE[m_modulusLen]);
        memcpy(ret->mp_modulus, mp_modulus, m_modulusLen);
    }
    else
        ret->mp_modulus = NULL;

    return ret;
}

// --------------------------------------------------------------------------------
//           decrypt a buffer
// --------------------------------------------------------------------------------

unsigned int WinCAPICryptoKeyRSA::privateDecrypt(const unsigned char * inBuf,
                                 unsigned char * plainBuf,
                                 unsigned int inLength,
                                 unsigned int maxOutLength,
                                 PaddingType padding,
                                 const XMLCh* hashURI,
                                 const XMLCh* mgfURI,
                                 unsigned char* params,
                                 unsigned int paramsLen) const {

    // Perform a decrypt
    if (m_key == NULL) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Attempt to decrypt data with empty key");
    }

    // Have to reverse ordering of input :
    DWORD decryptSize = inLength;
    // memcpy(plainBuf, inBuf, inLength);
    for (unsigned int i = 0; i < inLength; ++i) {
        plainBuf[i] = inBuf[inLength - 1 - i];
    }

    switch (padding) {

    case XSECCryptoKeyRSA::PAD_PKCS_1_5 :

        if (!CryptDecrypt(m_key,
                         0,
                         TRUE,
                         0,
                         plainBuf,
                         &decryptSize)) {

            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA privateKeyDecrypt - Error Decrypting PKCS1_5 padded RSA encrypt");
        }

        break;

    case XSECCryptoKeyRSA::PAD_OAEP :

        if (XSECAlgorithmSupport::getMGF1HashType(mgfURI) != XSECCryptoHash::HASH_SHA1) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm,
                "WinCAPI:RSA - Unsupported OAEP MGF algorithm");
        }

        if (XSECAlgorithmSupport::getHashType(hashURI) != XSECCryptoHash::HASH_SHA1) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm,
                "WinCAPI:RSA - Unsupported OAEP digest algorithm");
        }
        else if (paramsLen > 0) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedError,
                "WinCAPI::setOAEPParams - OAEP parameters are not supported by Windows Crypto API");
        }

        if (!CryptDecrypt(m_key,
                         0,
                         TRUE,
                         CRYPT_OAEP,
                         plainBuf,
                         &decryptSize)) {

            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA privateKeyDecrypt - Error Decrypting PKCS1v2 OAEP padded RSA encrypt");
        }

        break;


    default :
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WiNCAPI:RSA - Unknown padding method");
    }


    return decryptSize;
}

// --------------------------------------------------------------------------------
//           encrypt a buffer
// --------------------------------------------------------------------------------

unsigned int WinCAPICryptoKeyRSA::publicEncrypt(const unsigned char* inBuf,
                                 unsigned char* cipherBuf,
                                 unsigned int inLength,
                                 unsigned int maxOutLength,
                                 PaddingType padding,
                                 const XMLCh* hashURI,
                                 const XMLCh* mgfURI,
                                 unsigned char* params,
                                 unsigned int paramsLen) const {

    // Perform an encrypt
    if (m_key == 0) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Attempt to encrypt data with empty key");
    }

    DWORD encryptSize = inLength;
    memcpy(cipherBuf, inBuf, inLength);

    switch (padding) {

    case XSECCryptoKeyRSA::PAD_PKCS_1_5 :

        if (!CryptEncrypt(m_key,
                          0,            /* No Hash */
                          TRUE,            /* Is Final */
                          0,            /* No flags */
                          cipherBuf,
                          &encryptSize,
                          maxOutLength)) {

            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA publicKeyEncrypt - Error performing encrypt");
        }

        if (encryptSize <= 0) {
            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA publicKeyEncrypt - Error performing PKCS1_5 padded RSA encrypt");
        }

        break;

    case XSECCryptoKeyRSA::PAD_OAEP :

        if (XSECAlgorithmSupport::getHashType(hashURI) != XSECCryptoHash::HASH_SHA1) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm,
                "WinCAPI:RSA - OAEP padding method requires SHA-1 digest method");
        }
        else if (XSECAlgorithmSupport::getMGF1HashType(mgfURI) != XSECCryptoHash::HASH_SHA1) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm,
                "WinCAPI:RSA - Unsupported OAEP MGF algorithm");
        }
        else if (paramsLen > 0) {
            throw XSECCryptoException(XSECCryptoException::UnsupportedError,
                "WinCAPI::setOAEPParams - OAEP parameters are not supported by Windows Crypto API");
        }

        if (!CryptEncrypt(m_key,
                          0,            /* No Hash */
                          TRUE,            /* Is Final */
                          CRYPT_OAEP,
                          cipherBuf,
                          &encryptSize,
                          maxOutLength)) {

            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA publicKeyEncrypt - Error performing encrypt");
        }

        if (encryptSize <= 0) {
            throw XSECCryptoException(XSECCryptoException::RSAError,
                "WinCAPI:RSA publicKeyEncrypt - Error performing OAEP RSA encrypt");
        }

        break;

    default :

        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Unknown padding method");
    }

    // Reverse the output
    unsigned char *tbuf;
    XSECnew(tbuf, unsigned char[encryptSize]);
    ArrayJanitor<unsigned char> j_tbuf(tbuf);
    memcpy(tbuf, cipherBuf, encryptSize);

    for (unsigned int i = 0; i < encryptSize; ++i)
        cipherBuf[i] = tbuf[encryptSize - 1 - i];

    return encryptSize;
}

// --------------------------------------------------------------------------------
//           Size in bytes
// --------------------------------------------------------------------------------

unsigned int WinCAPICryptoKeyRSA::getLength() const {

    DWORD len;
    DWORD pLen = 4;

    if (!CryptGetKeyParam(m_key,
                     KP_BLOCKLEN,
                     (BYTE *) &len,
                     &pLen,
                     0)) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error determining key size");
    }

    return len / 8;
}

// --------------------------------------------------------------------------------
//           Some utility functions
// --------------------------------------------------------------------------------

void WinCAPICryptoKeyRSA::loadParamsFromKey() {

    if (m_key == 0) {

        if (m_keySpec == 0)
            return;

        // See of we can get the user key
        if (!CryptGetUserKey(m_p, m_keySpec, &m_key))
            return;
    }

    // Export key into a keyblob
    BOOL fResult;
    DWORD blobLen;

    fResult = CryptExportKey(
        m_key,
        0,
        PUBLICKEYBLOB,
        0,
        NULL,
        &blobLen);

    if (fResult == 0 || blobLen < 1) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error exporting public key");
    }

    BYTE * blob;
    XSECnew(blob, BYTE[blobLen]);
    ArrayJanitor<BYTE> j_blob(blob);

    fResult = CryptExportKey(
        m_key,
        0,
        PUBLICKEYBLOB,
        0,
        blob,
        &blobLen);

    if (fResult == 0 || blobLen < 1) {
        throw XSECCryptoException(XSECCryptoException::RSAError,
            "WinCAPI:RSA - Error exporting public key");
    }

    RSAPUBKEY * pk = (RSAPUBKEY *) ( blob + WINCAPI_BLOBHEADERLEN );
    DWORD keyLen = pk->bitlen / 8;

    // Copy the keys

    BYTE * i = (BYTE *) ( pk );
    i += WINCAPI_RSAPUBKEYLEN;
    if (mp_modulus != NULL)
        delete[] mp_modulus;

    m_modulusLen = keyLen;
    XSECnew(mp_modulus, BYTE[m_modulusLen]);
    memcpy(mp_modulus, i, m_modulusLen);

    // Take the simple way out
    XSECnew(mp_exponent, BYTE[4]);
    *((DWORD *) mp_exponent) = pk->pubexp;

    // Now cut any leading 0s (Windows is LE, so start least significant end)

    m_exponentLen = 3;
    while (m_exponentLen > 0 && mp_exponent[m_exponentLen] == 0)
        m_exponentLen--;

    m_exponentLen++;    // Make it a length as apposed to an offset
}

unsigned int WinCAPICryptoKeyRSA::getExponentBase64BigNums(char* b64, unsigned int len) {

    if (m_key == 0 && m_keySpec == 0 && mp_exponent == NULL) {

        return 0;    // Nothing we can do

    }

    if (mp_exponent == NULL) {

        loadParamsFromKey();

    }

    unsigned int bLen;
    unsigned char * b =  WinCAPICryptoProvider::WinBN2b64(mp_exponent, m_exponentLen, bLen);
    if (bLen > len)
        bLen = len;
    memcpy(b64, b, bLen);
    delete[] b;

    return bLen;
}

unsigned int WinCAPICryptoKeyRSA::getModulusBase64BigNums(char* b64, unsigned int len) {

    if (m_key == 0 && m_keySpec == 0 && mp_modulus == NULL) {

        return 0;    // Nothing we can do

    }

    if (mp_modulus == NULL) {

        loadParamsFromKey();

    }

    unsigned int bLen;
    unsigned char * b =  WinCAPICryptoProvider::WinBN2b64(mp_modulus, m_modulusLen, bLen);
    if (bLen > len)
        bLen = len;
    memcpy(b64, b, bLen);
    delete[] b;

    return bLen;
}

#endif /* WINCAPI */
