0

2048 ビットの RSA 公開鍵と秘密鍵のペアを生成する状況があります。公開鍵はクライアント コンピューター (Windows) にコピーされ、秘密鍵はサーバー (Linux) に保持されます。サーバーが秘密鍵を使用してデータのブロックに署名を生成し、クライアントが公開鍵を使用して署名を検証できるように、署名とデータのブロックをクライアントに送信できるようにします。

キーの生成方法は次のとおりです。

openssl genrsa -out private.key 2048
openssl req -subj "/C=GB/ST=ST/L=L/O=Org/OU=Unit/CN=cert name/emailAddress=nothing@nowhere" -new -x509 -key private.key -out publickey.cer -days 3650
openssl pkcs12 -passout pass:mypassword -export -nokeys -out public.key -in publickey.cer -inkey private.key

これにより、private.key (公開鍵と秘密鍵を組み合わせたもの) と public.key (公開鍵) の 2 つの鍵ファイルが作成されます。

クライアント アプリケーションは C# です。そこで、次のように公開鍵をロードします。

X509Certificate2 cert = new X509Certificate2(@"D:\public.key", "mypassword");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;

これは、データのブロックを暗号化してソケット接続でサーバーに送信すると、サーバーがそれを復号化してデータの内容を正しく表示できるため、暗号化には問題なく機能します。したがって、キーが正しく読み込まれていると確信しています。

サーバーが暗号化されたデータ ブロックを取得し、それを正しく復号化すると、応答に署名してクライアントに送り返します。

// Send a signed reply
unsigned char *signature = (unsigned char *) malloc(RSA_size(privateKey));
if (signature != NULL)
{
    char *reply ="Top of the morning to you";
    unsigned int siglen;
    if (RSA_sign(NID_sha1, (unsigned char *)reply, strlen(reply), signature, &siglen, privateKey))
    {
        printf("SigLen = %d\n", siglen);
        unsigned char *msg = (unsigned char *)malloc(siglen + strlen(reply));
        if (msg != NULL)
        {
            memcpy(msg, signature, siglen);
            memcpy(msg+siglen, reply, strlen(reply));
            send(sock, msg, siglen + strlen(reply), 0);
            free(msg);
        }
    }
    free(signature);
}

クライアントはデータを受信し、署名されているデータから署名を分離し、署名を検証しようとしますが、これは常に失敗します。クライアント部分の完全なコードは次のとおりです。

static void Main(string[] args)
{
    X509Certificate2 cert = new X509Certificate2(@"D:\public.key", "mypassword");
    RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;

    string payload = "Hello world!!";

    byte[] encrypted = rsa.Encrypt(Encoding.ASCII.GetBytes(payload), false);

    TcpClient client = new TcpClient();
    client.Connect("192.168.1.57", 2002);

    NetworkStream ns = client.GetStream();

    ns.Write(encrypted, 0, encrypted.Length);
    ns.ReadTimeout = 2000;
    byte[] rx = new byte[5000];
    int bytesRead = ns.Read(rx, 0, rx.Length);
    if (bytesRead > 0)
    {
        int keySizeBytes = rsa.KeySize >> 3;
        if (bytesRead > keySizeBytes)
        {
            byte[] signedMessage = new byte[bytesRead - keySizeBytes];
            Array.Copy(rx, keySizeBytes, signedMessage, 0, signedMessage.Length);
            Array.Resize<byte>(ref rx, keySizeBytes);

            if (rsa.VerifyData(signedMessage, CryptoConfig.MapNameToOID("SHA1"), rx))
            {
                Console.WriteLine("Verified OK");
            }
            else
            {
                Console.WriteLine("Not verified");
            }
        }
    }
    client.Close();
    Console.ReadLine();
}

クライアント側でデータを検証できるようにするために必要なことはありますか?

4

1 に答える 1

0

あなたはRSA_sign間違って使用しています-ドキュメントごと(強調鉱山):

RSA_sign() は、PKCS #1 v2.0 で指定されている秘密鍵 rsa を使用して、サイズ m_lenのメッセージ ダイジェストm に署名します。

つまり、最初にメッセージをハッシュしてから、メッセージ ハッシュを に渡す必要がありますRSA_sign。その後、.NET で検証できるようになります。

次の変更が機能するはずです。

unsigned char* hash = SHA1((unsigned char *)reply, strlen(reply), NULL);

if (RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, signature, &siglen, privateKey))
{
    ...
于 2013-09-12T16:46:01.580 に答える