【问题标题】:Signing data with RSA key使用 RSA 密钥对数据进行签名
【发布时间】:2018-05-22 08:17:14
【问题描述】:

我有可以正常工作的 openssl 命令:

echo -n "my_password" | openssl pkeyutl -sign -inkey /home/ramunas/Downloads/id_rsa | base64 -w 0

并输出所需的结果:

Q9dyYKFvGOELAZYN8O18l30/bC/hVBm+7sy0DFJkspSvQa4bY7xGGsRina99ivxbdsu4BaakXDdA/X9wxP/Ll/GpEUS9xteiVNQkOEYOl27OAhe0OsC6zRDKpRPJEFhfLVt0cHlDILRZ0yuJLfv0UiwF0CTe4Xv5AN3RXrv28nIS8XA0o/TmCB5XlG12Zq7UdFoDF+j6oFXMyWAV9eXa1uQdNzIAm8wPn4e3lEz4v8p/M5PhLlAO2rBTEw79PiqFtKqlJ1OAZan8As02ejBjpe3t55dKuQke5+S1/7Zt7bZozDeswSMYbb643ZbLW+QN0AoO7pMy4w8qugzQZ/gnfw==

RSA 密钥 (PKCS8):

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfHlwo+7jE6TKGGquuLDcrj+gPiWpl7QKpUAa8rqCs+A0iGbIM843U+uRx5mAhoBSJhBL9XPZss+IEKGy29K6bdy8sCXBvU+rjjPC/q91Sg5pw84063h8sOvV0UIFZVFZBt4Ax53qhOWFMAGtNB55LPvih71uz+iI5KneeS6OgAeBy1I+fJG1SiFDyOZsugma4f50ACXtI4hVtYSmM3/hY3XpWGQdUJPKByTfIxHg1v6EdMZ7mlOjYSFZxCVQRSXUAVf9ctPQQ4iA4gn69uveYH/fZQTc+7M3FGGsYVY2q31N+3jRbppOi6NqqISPckfXKnlTNkTkjXM0ZbZSvJrk3AgMBAAECggEAZjItpvTlmzLbjF4c3eTwGj53IWN0vroW93+6k/zknbNDXB98x+y1r5DkoHMCUxZpmlkFiUznyLFP/LOp/2fvrVJDLdlyKU6Qkk23YqOTpqd3zgvfLWv0QYgsleQO77zHGbYt09+EV4BvCU1LnVHDTTjIn9kH4PRKfsTTxwt1sBH//kXwZaRlvKxP0C9EGXPCymgdFO9RAhaVURDx3x7prKGRJbPDSMIP09elhChQ2fHvTDlqvrnr71hIR1ge8uGMkRta0gv3VyXpbAOrVJqxzmiccUIQoocWVLNCPXFZTe/7BFx1esLH4nGUwC57yiw9KudVhl5oeMIcHjKyrnoeIQKBgQDeuGqEqvGzlX/ZTt9S9LBj04OMA+0T2Lg/rg8pbn3MsqCLPikaf55Xpx5Le8fgc9cQzZYaLb22bEMAkl38EEttTK0ChWFnViGVYrxTDuQZA2eKljLrmuiynrjOdJq1Sro7bRsSOPYO32BwdGZ2/KJCJkocgRP2Mi9nALlrBfcRswKBgQC25QZIeynG2DHSR6rUTNhurVqbVHE5CWfmABdUHyg90y0DSjeKLad3z42Zln5mKfcapjWqL4443HAJ8ggdkyyYuif0RO4L2NBBKL/GxSjpeRgEZfmhOMgJLEy9dAzWSaiH1B5/MY1ZWxXWJBinOgu7ME6rrK5pEZDq/ySApkMQbQKBgBPx9fANkvmfIMToME2whf9amMQ4Mcn+NXnWb5spPvDO6lut5GZwGVEGMUtXOW4bUZ46mv+bKwskJNfvQ6VoHCkcnb3FDhT//J/xKKZThg/LY0Fg9AxvYfllB23NuXEU0RqzSaJXIYnxbSSE+Awd0bYU0bw9vvjkQ6R4xyITda3LAoGBALEcB4GZA1OzeEuRazQ7VjuXU+6nTx1UpnBsFis4INf979EHxdDhdRLows7AlZaJfOkpmz463xbhIP4AiytYog8j9hztwzdltgXjWBM8SeoNgdyAg8REIDIB3C56exPoMHOYThAOBDtLoVHFkrod085pcUV960eD4zot6UNLvhXhAoGBAITXSidOCGfkUVj8/oItR/IV+gwqFZFMoXDD8vJ/0G99Rjib4rWcX6B3NxamnNA6EfiGaMTdWZaxFbJOPh2K7OcOlDCWNpMvEQQyI0nhGKkKCujiTfBkSWkKDHdEAC/p0k2rAh4feYabdvX+ePmWAs6/X/syQ69jBk2Y6PhUsJH3

但我需要在this answer 的帮助下使用 C# 对密码进行签名,我创建了 RSA 提供程序。这与示例中的大部分相同,但需要在一个地方添加字节搜索,我怀疑与链接中的密钥相比,密钥具有更多信息。顺便说一句,我用openssl和c#输出模数部分,除了第一个十六进制数字

private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
    {
        var privateKeyBits = System.Convert.FromBase64String(privateKey);

        var RSA = new RSACryptoServiceProvider();
        var RSAparams = new RSAParameters();

        using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
        {
            byte bt = 0;
            ushort twobytes = 0;
            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130)
                binr.ReadByte();
            else if (twobytes == 0x8230)
                binr.ReadInt16();
            else
                throw new Exception("Unexpected value read binr.ReadUInt16()");

            twobytes = binr.ReadUInt16();
            if (twobytes != 0x0102)
                throw new Exception("Unexpected version");

            bt = binr.ReadByte();
            if (bt != 0x00)
                throw new Exception("Unexpected value read binr.ReadByte()");

            for (int ii = 0; ii < 26; ii++) //!!! this one needs explanation
                binr.Read();

            RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
            RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
        }

        RSA.ImportParameters(RSAparams);
        return RSA;
    }

    private int GetIntegerSize(BinaryReader binr)
    {
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)
            return 0;
        bt = binr.ReadByte();

        if (bt == 0x81)
            count = binr.ReadByte();
        else
            if (bt == 0x82)
        {
            highbyte = binr.ReadByte();
            lowbyte = binr.ReadByte();
            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
            count = BitConverter.ToInt32(modint, 0);
        }
        else
        {
            count = bt;
        }

        while (binr.ReadByte() == 0x00)
        {
            count -= 1;
        }
        binr.BaseStream.Seek(-1, SeekOrigin.Current);
        return count;
    }

通过这个提供者,我尝试“签署”我的数据,但结果与 openssl 不匹配

public void Test()
{
   RSACryptoServiceProvider provider = CreateRsaProviderFromPrivateKey(idRsa);
   string DataToEncrypt = "my_password";
   List<string> listStr = new List<string>();
   Encoding enco = Encoding.UTF8;
   listStr.Add(Convert.ToBase64String(provider.SignData(enco.GetBytes(DataToEncrypt), "SHA1")));
    listStr.Add(Convert.ToBase64String(provider.SignData(enco.GetBytes(DataToEncrypt), "SHA256")));
    listStr.Add(Convert.ToBase64String(provider.SignData(enco.GetBytes(DataToEncrypt), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)));
   listStr.Add(Convert.ToBase64String(provider.Encrypt(enco.GetBytes(DataToEncrypt), true)));
   listStr.Add(Convert.ToBase64String(provider.Encrypt(enco.GetBytes(DataToEncrypt), false)));
   listStr.Add(Convert.ToBase64String(provider.Encrypt(enco.GetBytes(DataToEncrypt), RSAEncryptionPadding.OaepSHA1)));
}

【问题讨论】:

标签: c# .net openssl cryptography rsa


【解决方案1】:

通过在 Windows 中使用 OpenSSL 解决了问题(在此 tutorial 的帮助下下载并构建二进制文件)。唯一的区别是您必须传递密码文件并且无法从命令行读取输出(命令行输出不匹配文件输出并且不正确)。我的代码如下:

Encoding encoding = Encoding.Default;
string openSSLExe = "C:\\OpenSSL-Win32\\bin\\openssl.exe";


string unencodedPsw = "something";
string pkey = "MIIEvAIBADANB...";        //It comes from database, therefore needs to save each time.
string pwdFile = Path.GetTempFileName();
string rsaFile = Path.GetTempFileName();
string outFile = Path.GetTempFileName();

File.WriteAllBytes(pwdFile, encoding.GetBytes(unencodedPsw));
File.WriteAllBytes(rsaFile,  encoding.GetBytes(pkey));

string args = String.Format("pkeyutl -sign -in \"{0}\" -inkey \"{1}\" -out \"{2}\"", pwdFile, rsaFile, outFile);
var proc = new Process
{
    StartInfo = new ProcessStartInfo
    {
        FileName = openSSLExe,
        Arguments = args,
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

proc.Start();
proc.WaitForExit();

string encodedPsw = Convert.ToBase64String(File.ReadAllBytes(outFile));
File.Delete(pwdFile);
File.Delete(rsaFile);
File.Delete(outFile);

return encodedPsw;

【讨论】:

  • 我遇到了完全相同的问题,您的代码对我帮助很大!谢谢!
【解决方案2】:

问题是openssl pkeyutl -sign 并没有按照你的想法去做。 (你想要openssl dgst -sign

$ echo -n "my_password" | \
> openssl pkeyutl -sign -inkey rsa.key | \
> openssl rsautl -inkey rsa.key -verify -raw -hexdump
0000 - 00 01 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0010 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0020 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0030 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0040 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0050 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0060 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0070 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0080 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0090 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00a0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00b0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00c0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00d0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00e0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00f0 - ff ff ff ff 00 6d 79 5f-70 61 73 73 77 6f 72 64   .....my_password

请注意,它在填充块的末尾包含my_password,而不是该值的摘要。

openssl pkeyutl -sign 将输入作为摘要后格式化的输入数据,在 PKCS#1 签名的情况下,它应该是一个 DER 编码的 DigestInfo,描述使用了哪种摘要算法以及生成的摘要是什么 (https://www.rfc-editor.org/rfc/rfc2437#section-9.2.1第2步)。这几乎意味着您永远不想使用该命令。

将此与openssl dgst -sign 的输出进行比较:

$ echo -n "my_password" | \
> openssl dgst -sha256 -sign rsa.key | \
> openssl rsautl -verify -raw -hexdump -inkey rsa.key
0000 - 00 01 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0010 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0020 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0030 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0040 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0050 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0060 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0070 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0080 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0090 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00a0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00b0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00c0 - ff ff ff ff ff ff ff ff-ff ff ff ff 00 30 31 30   .............010
00d0 - 0d 06 09 60 86 48 01 65-03 04 02 01 05 00 04 20   ...`.H.e.......
00e0 - f6 e2 48 ea 99 4f 3e 34-2f 61 14 1b 8b 8e 3e de   ..H..O>4/a....>.
00f0 - 86 d4 de 53 25 7a bc 8d-06 ae 07 a1 da 73 fb 39   ...S%z.......s.9

如果我们改为将openssl dgst -sign 的输出发送到base64

GP0L9sIrk0QV6r+ZfP42yq9pWGDF5OwdoStvolp+DKzSxpc+eSItpIJrt6VIkfOMeyJG3GZUSDf2
Z5iLTzZ+NAQBj0ay0Y0FH81rg2oTAtJ/vbh5+jB3Akgkct8xosJpjJDNaDIUb6gN+QcCS0g1Nbsn
YFetFoH7lX2oDmkaAWdiBquLmCdiA8lsRGs9YocuhDghj36TCjvrqS0ZheX0Oa1rM9xFMFATdFTg
BJffGWVoZzikZ6orVG0BZ2XOh6DvxBnjqZZQ40ru20bfq40qT1iZmf72bQ7/rayOcVNxDi09UV6+
kXYSz++mJvzpOe+MIwaFlrZVUlcgDXvKhZtbRQ==

这应该与您在 .NET 中使用带有该密钥的 SHA256 签名时获得的值相同。

【讨论】:

  • 我会试试这个。我使用 pkeyutl 只是因为我必须对休息服务的密码进行编码,服务提供商提供了这样的编码示例。
  • @Ramunas PKCS8 是关于传输私钥的。所以这只是你的 CreateRsaProviderFromPrivateKey 方法。
猜你喜欢
  • 2014-01-13
  • 2012-07-15
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-08
相关资源
最近更新 更多