如何在WinRT中加密和解密常量字符*

How to encrypt and decrypt a const char* in WinRT

本文关键字:解密 常量 字符 加密 WinRT      更新时间:2023-10-16

我一直在尝试编写加密和解密函数,这些函数的签名只要求输入和输出字符串为void*类型。如果输入可以指定为IBuffer^,但在另一种情况下,源字符串和加密->解密的字符串不匹配,则代码工作正常。

CodeIBuffer^ byteArrayToIBufferPtr(byte *source, int size)
{
    Platform::ArrayReference<uint8> blobArray(source, size);
    IBuffer ^buffer = CryptographicBuffer::CreateFromByteArray(blobArray);
    return buffer;
}
byte* IBufferPtrToByteArray(IBuffer ^buffer)
{
    Array<unsigned char,1U> ^platArray = ref new Array<unsigned char,1U>(256);
    CryptographicBuffer::CopyToByteArray(buffer,&platArray);
    byte *dest = platArray->Data;
    return dest;
}
int DataEncryption::encryptData(EncryptionAlgorithm algo, int keySize, void* srcData, const unsigned int srcSize,
        void*& encData, unsigned int& encSize)
{
    LOG_D(TAG, "encryptData()");
    if(srcData == nullptr)
    {
        LOG_E(TAG,"");
        return DataEncryption::RESULT_EMPTY_DATA_ERROR;
    }
    if(srcSize == 0)
    {
        LOG_E(TAG,"");
        return DataEncryption::RESULT_SIZE_ZERO_ERROR;
    }
    IBuffer^ encrypted;
    IBuffer^ buffer;
    IBuffer^ iv = nullptr;
    String^ algName;
    bool cbc = false;
    switch (algo)
    {
    case DataEncryption::ENC_DEFAULT:
        algName = "AES_CBC";
        cbc = true;
        break;
    default:
        break;
    }
    // Open the algorithm provider for the algorithm specified on input.
    SymmetricKeyAlgorithmProvider^ Algorithm = SymmetricKeyAlgorithmProvider::OpenAlgorithm(algName);
    // Generate a symmetric key.
    IBuffer^ keymaterial = CryptographicBuffer::GenerateRandom((keySize + 7) / 8);
    CryptographicKey^ key;
    try
    {
        key = Algorithm->CreateSymmetricKey(keymaterial);
    }
    catch(InvalidArgumentException^ e)
    {
        LOG_E(TAG,"encryptData(): Could not create key.");
        return DataEncryption::RESULT_ERROR;
    }
    // CBC mode needs Initialization vector, here just random data.
    // IV property will be set on "Encrypted".
    if (cbc)
        iv = CryptographicBuffer::GenerateRandom(Algorithm->BlockLength);
    // Set the data to encrypt. 
    IBuffer ^srcDataBuffer = byteArrayToIBufferPtr(static_cast<byte*>(srcData),256);
    // Encrypt and create an authenticated tag.
    encrypted = CryptographicEngine::Encrypt(key, srcDataBuffer, iv);
    //encData = encrypted;
    byte *bb = IBufferPtrToByteArray(encrypted);
    encData = IBufferPtrToByteArray(encrypted);
    encSize = encrypted->Length;
    return DataEncryption::RESULT_SUCCESS;
}

int DataEncryption::decryptData(EncryptionAlgorithm algo, int keySize, void* encData, const unsigned int encSize,
        void*& decData, unsigned int& decSize)
{
    LOG_D(TAG, "decryptData()");
    if(encData == nullptr)
    {
        LOG_E(TAG,"");
        return DataEncryption::RESULT_EMPTY_DATA_ERROR;
    }
    if(encSize == 0)
    {
        LOG_E(TAG,"");
        return DataEncryption::RESULT_SIZE_ZERO_ERROR;
    }
    IBuffer^ encrypted;
    IBuffer^ decrypted;
    IBuffer^ iv = nullptr;
    String^ algName;
    bool cbc = false;
    switch (algo)
    {
    case DataEncryption::ENC_DEFAULT:
        algName = "AES_CBC";
        cbc = true;
        break;
    default:
        break;
    }
    // Open the algorithm provider for the algorithm specified on input.
    SymmetricKeyAlgorithmProvider^ Algorithm = SymmetricKeyAlgorithmProvider::OpenAlgorithm(algName);
    // Generate a symmetric key.
    IBuffer^ keymaterial = CryptographicBuffer::GenerateRandom((keySize + 7) / 8);
    CryptographicKey^ key;
    try
    {
        key = Algorithm->CreateSymmetricKey(keymaterial);
    }
    catch(InvalidArgumentException^ e)
    {
        LOG_E(TAG,"encryptData(): Could not create key.");
        return DataEncryption::RESULT_ERROR;
    }
    // CBC mode needs Initialization vector, here just random data.
    // IV property will be set on "Encrypted".
    if (cbc)
        iv = CryptographicBuffer::GenerateRandom(Algorithm->BlockLength);
    // Set the data to decrypt. 
    byte *cc = static_cast<byte*>(encData);
    IBuffer ^encDataBuffer = byteArrayToIBufferPtr(cc,256);
    // Decrypt and verify the authenticated tag.
    decrypted = CryptographicEngine::Decrypt(key, encDataBuffer, iv);
    byte *bb = IBufferPtrToByteArray(decrypted);
    decData = IBufferPtrToByteArray(decrypted);
    decSize = decrypted->Length;
    return DataEncryption::RESULT_SUCCESS;
}

我猜问题出在这个函数上:

byte* IBufferPtrToByteArray(IBuffer ^buffer)
{
    Array<unsigned char,1U> ^platArray = ref new Array<unsigned char,1U>(256);
    CryptographicBuffer::CopyToByteArray(buffer,&platArray);
    byte *dest = platArray->Data;
    return dest;
}

您在那里所做的是分配一个具有1个引用的新Platform::Array<byte>^,然后获取一个指向其内部托管存储的指针,然后返回该指针——此时阵列将被取消引用,从而释放其底层存储。因此,您返回的指针指向释放的内存。下一次分配可能会覆盖这些字节。

您需要做的是通过引用CopyToByteArray()中的Array<byte>^获取返回(这将创建一个新的数组,可能会包装输入IBuffer^的字节并返回它(,然后复制该数组的内容

您的最终结果将类似于Readium SDK项目中的这个片段,该项目获取一个std::string实例,使用SHA-1对其进行散列,并将散列数据复制到成员变量uint8_t _key[KeySize]:中

using namespace ::Platform;
using namespace ::Windows::Foundation::Cryptography;
using namespace ::Windows::Foundation::Cryptography::Core;
auto byteArray = ArrayReference<byte>(reinterpret_cast<byte*>(const_cast<char*>(str.data())), str.length());
auto inBuf = CryptographicBuffer::CreateFromByteArray(byteArray);
auto keyBuf = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->HashData(inBuf);
Array<byte>^ outArray = nullptr;
CryptographicBuffer::CopyToByteArray(keyBuf, &outArray);
memcpy_s(_key, KeySize, outArray->Data, outArray->Length);

步骤:

  1. 创建与std::string中的字节相对应的ArrayReference<byte>(无复制(
  2. 将其传递给CryptographicBuffer::CreateFromByteArray()以获得您的IBuffer^。仍然没有复制数据
  3. 调用您的hash/加密函数,传递您刚刚创建的IBuffer^。作为回报,您会得到另一个IBuffer^,它可能使用也可能不使用完全相同的存储(我认为这实际上取决于算法的实现(
  4. 创建一个类型为Array<byte>^的变量。不要分配对象,您将通过引用获得一个对象
  5. 将该对象的地址传递到CryptographicBuffer::CopyToByteArray()以接收密钥数据的副本
  6. Array^仍然有效时,将其字节复制到您的本机数组中