由OpenSSL签名的数据的.Net RSA验证

本文关键字:Net RSA 验证 数据 OpenSSL | 更新日期: 2023-09-27 18:31:00

我有一个情况,我生成了一个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

这将创建两个密钥文件,私有.key(公钥和私钥的组合)和公钥.key(公钥)。

客户端应用程序采用 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();
}

我是否需要执行哪些操作才能验证客户端的数据?

由OpenSSL签名的数据的.Net RSA验证

您错误

地使用了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))
{
    ...