2

C++ で書き直さなければならない C# での暗号化用のコードがいくつかあります。SO でいくつかの同様の質問を見ましたが、どういうわけかまだこれを理解できませんでした。同じパスワードで同じ文字列をエンコードすると、異なる結果が得られます。

C# コード

    byte[] TestEncrypt(string data)
    {
        byte[] plainText  = System.Text.Encoding.ASCII.GetBytes(data);
        TripleDES des3 = new     System.Security.Cryptography.TripleDESCryptoServiceProvider();
        des3.Mode = CipherMode.CBC;
        des3.Key = System.Text.Encoding.ASCII.GetBytes("12656b2e4ba2f22e");
        des3.IV = System.Text.Encoding.ASCII.GetBytes("d566gdbc");
        ICryptoTransform transform = des3.CreateEncryptor();
        MemoryStream memStreamEncryptedData = new MemoryStream();
        CryptoStream encStream = new CryptoStream(memStreamEncryptedData,
            transform, CryptoStreamMode.Write);
        encStream.Write(plainText, 0, plainText.Length);
        encStream.FlushFinalBlock();
        encStream.Close();
        byte[] cipherText = memStreamEncryptedData.ToArray();
        return cipherText;
    }

結果 255,142,22,151,93,255,156,10,174,10,250,92,144,0,60,142 EDITED: 新しい C++ バージョンを追加

    string Test3DES()
    {
        string key = "12656b2e4ba2f22e";
        HCRYPTPROV hCryptProv = NULL;
        HCRYPTHASH hHash = NULL;
        HCRYPTKEY hCryptKey = NULL;
        char pIV[] = "d566gdbc";  //simple test IV for 3DES
        CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);
        PlainTextKeyBlob keyBlob ={0};
        keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
        keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
        keyBlob.hdr.reserved = 0;
        keyBlob.hdr.aiKeyAlg = CALG_3DES_112;
        keyBlob.cbKeySize = key.size();
        memcpy(keyBlob.key, key.c_str(), key.size());
        DWORD dwSizeBlob = sizeof(BLOBHEADER)+sizeof(DWORD)+key.size();
        ret = CryptImportKey( hCryptProv, (const BYTE*)&keyBlob, dwSizeBlob, 0, CRYPT_EXPORTABLE, &hCryptKey );
        DWORD dwMode = CRYPT_MODE_CBC;
        CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0);
        CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 
        DWORD dwFilled = 0;
        BOOL ret = CryptEncrypt( hCryptKey, NULL, TRUE, 0, (LPBYTE)cipherText.c_str(), &dwFilled, (DWORD)str.size());
        cipherText.resize(dwFilled);
        if( hCryptKey ) CryptDestroyKey( hCryptKey );
        if( hHash ) CryptDestroyHash( hHash );
        if( hCryptProv ) CryptReleaseContext( hCryptProv, 0 );
        return cipherText;
    }

結果 167,177,201,56,123,240,169,174

古い C++ バージョン

C++

  string Test3DES()
    {
        string key = "12656b2e4ba2f22e";
        HCRYPTPROV hCryptProv = NULL;
        HCRYPTHASH hHash = NULL;
        HCRYPTKEY hCryptKey = NULL;
        char pIV[] = "d566gdbc";  //simple test IV for 3DES
        CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
        CryptCreateHash( hCryptProv, CALG_MD5, NULL, 0, &hHash );
        CryptHashData( hHash, (LPBYTE)key.c_str(), (DWORD)key.size(), 0 ); 
        DWORD dwMode = CRYPT_MODE_CBC;
        CryptDeriveKey(hCryptProv, CALG_3DES, hHash, 0, &hCryptKey);
        CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0);
        CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 
        DWORD dwFilled = 0;
        BOOL ret = CryptEncrypt( hCryptKey, NULL, TRUE, 0, (LPBYTE)cipherText.c_str(), &dwFilled, (DWORD)str.size());
        cipherText.resize(dwFilled);
        if( hCryptKey ) CryptDestroyKey( hCryptKey );
        if( hHash ) CryptDestroyHash( hHash );
        if( hCryptProv ) CryptReleaseContext( hCryptProv, 0 );
        return cipherText;
    }
4

4 に答える 4

8

あなたのコードから始まるいくつかのサンプル プロジェクトをセットアップします。すべてが含まれていなかったので、いくつか追加する必要がありました。コンパイルしてテストするまでに、C++ と C# の両方で同じ答えを得ていました。cipherText バッファの指定方法に問題があるのではないでしょうか? これは私のテストコードのすべてなので、サンプルプロジェクトをセットアップして同じ結果が得られるかどうかを確認するのは簡単なはずです。そこから理解できるかもしれません:

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace _3dtest
{
    class Program
    {
        static byte[] TestEncrypt(string data)
        {
            byte[] plainText = System.Text.Encoding.ASCII.GetBytes(data);
            TripleDES des3 = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
            des3.Mode = CipherMode.CBC;
            des3.Key = System.Text.Encoding.ASCII.GetBytes("12656b2e4ba2f22e");
            des3.IV = System.Text.Encoding.ASCII.GetBytes("d566gdbc");
            ICryptoTransform transform = des3.CreateEncryptor();
            MemoryStream memStreamEncryptedData = new MemoryStream();
            CryptoStream encStream = new CryptoStream(memStreamEncryptedData,
                transform, CryptoStreamMode.Write);
            encStream.Write(plainText, 0, plainText.Length);
            encStream.FlushFinalBlock();
            encStream.Close();
            byte[] cipherText = memStreamEncryptedData.ToArray();
            return cipherText;
        }

        static void Main(string[] args)
        {
            var info = TestEncrypt("password");
            foreach (byte b in info)
            {
                Console.Write(b.ToString());
                Console.Write(", ");
            }
            Console.WriteLine();
        }
    }
}

C++

#include "stdafx.h"
#include <Windows.h>
#include <WinCrypt.h>
#include <cassert>
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

using namespace std;

struct PlainTextKeyBlob {
        BLOBHEADER hdr;
        DWORD cbKeySize;
        BYTE key[16];
};

std::wstring LastError(DWORD lasterr)
{
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
    return (wchar_t*)lpMsgBuf; // Leaking, don't care
}

std::vector<BYTE> Test3DES(const std::string& passwd)
{
        string key = "12656b2e4ba2f22e";
        unsigned char pIV[] = "d566gdbc";  //simple test IV for 3DES
        HCRYPTPROV hCryptProv = NULL;
        HCRYPTHASH hHash = NULL;
        HCRYPTKEY hCryptKey = NULL;
        DWORD ret = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);
        if( ret == 0 ) std::wcout << LastError(GetLastError()) << std::endl;

        PlainTextKeyBlob keyBlob ={0};
        keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
        keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
        keyBlob.hdr.reserved = 0;
        keyBlob.hdr.aiKeyAlg = CALG_3DES_112;
        keyBlob.cbKeySize =  key.size();
        memcpy(keyBlob.key, key.c_str(), key.size());

        DWORD dwSizeBlob = sizeof(BLOBHEADER)+sizeof(DWORD)+key.size();
        ret = CryptImportKey( hCryptProv, (const BYTE*)&keyBlob, dwSizeBlob, 0, CRYPT_EXPORTABLE, &hCryptKey );
        if( ret == 0 ) std::wcout << LastError(GetLastError()) << std::endl;

        DWORD dwMode = CRYPT_MODE_CBC;
        CryptSetKeyParam(hCryptKey, KP_MODE, (BYTE*)&dwMode, 0);
        CryptSetKeyParam(hCryptKey, KP_IV,(const BYTE*) pIV, 0) ; 

        std::vector< BYTE > buffer( 1024 );
        memcpy( &buffer[0], passwd.c_str(), passwd.size() );
        DWORD dwFilled = passwd.size();
        ret = CryptEncrypt( hCryptKey, NULL, TRUE, 0, (LPBYTE)&buffer[0], &dwFilled, (DWORD)buffer.size());
        if( ret == 0 ) std::wcout << LastError(GetLastError()) << std::endl;
        buffer.resize(dwFilled);
        if( hCryptKey ) CryptDestroyKey( hCryptKey );
        if( hHash ) CryptDestroyHash( hHash );
        if( hCryptProv ) CryptReleaseContext( hCryptProv, 0 );
        return buffer;
}

int _tmain(int argc, _TCHAR* argv[])
{
    auto result = Test3DES("password");
    std::for_each( begin(result), end(result), [](BYTE b) {
        cout << to_string( (_ULonglong)b ) << " ";
    });
    cout << std::endl;

    return 0;
}
于 2012-05-01T00:24:49.323 に答える
1

したがって、キーSystem.Text.Encoding.ASCII.GetBytes("5656b2e4ba2f22e")は 15 バイトです。これは、16 バイトまたは 24 バイトが必要なトリプル DES の有効なキー長ではありません。私の推測では、2 つの実装は、異なる方法でより大きなキーが必要であるという事実を補っています。

PS

Key と IV に使用する ascii 定数文字列の未加工のバイト値を取得するのは非常に奇妙です。あなたの例では、キーと IV は文字列内の 16 進数です。2文字ごとに可能なバイトの全範囲を表し、16進数変換器を使用して変換できるように、必要なバイト長の2倍にする必要があるようです.16 進文字列をバイト配列に変換するにはどうすればよいですか? C++ は文字列を 16 進数に、またはその逆に変換します

アルゴリズムの ASCII バイトのみを取得する 16 進数で表されたキーを常に持つことのセキュリティ上の意味は、可能なキースペースを平方根で削減していることです!

于 2012-04-25T13:28:08.963 に答える
0

C ++はワイド/ユニコード文字列でコンパイルされているため、キーとして文字列に対応するワイド/ユニコードバイト"5656b2e4ba2f22e"(おそらく30バイト)を使用していますが、C#ではASCIIエンコーディングを使用して同じ文字列をバイトに変換しているため、次のようになります。同じ文字列のASCIIバイト(15バイト)。

C#でUnicodeエンコーディングを使用するか、キーを文字列ではなくバイトの配列として宣言することをお勧めします。

編集

間違った答え-string常に1文字あたり8ビットであるため、エンコーディングの違いが原因で問題が発生することはありません。

于 2012-04-21T11:57:11.873 に答える
0

DES キーは自己パリティ キーであることに注意してください。つまり、キーが正しいかどうかを確認するために、各バイトの 1 ビットが使用されます。おそらく、これらのビットが間違っていますか?DES ウィキペディアのページを使用して、それを見つけることができるはずです。

また、TDES が機能するには、少なくとも 2 つの完全な (64 ビット) キーが必要です。

于 2012-04-25T14:04:16.377 に答える