简体   繁体   中英

Signing data with RSA key

I have openssl command which works fine:

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

and outputs required result:

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

RSA Key (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

But i need to sign password with C#, with a help from this answer i created RSA provider. Which is mostly same as in example but required to add byte seek in one place, i suspect key has some more info compared to key in link. By the way i output modulus part in key with openssl and from c# they match except first hex number 模量比较

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;
    }

With this provider i try to "sign" my data, but result do not match with 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)));
}

The problem is that openssl pkeyutl -sign doesn't do what you think it does. (You wanted 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

Note that it contained my_password at the end of the padded block, not a digest of that value.

openssl pkeyutl -sign takes the input as the post-digest formatted input data, in the case of PKCS#1 signing it should be a DER-Encoded DigestInfo describing which digest algorithm was used and what the resulting digest was ( https://tools.ietf.org/html/rfc2437#section-9.2.1 step 2). This pretty much means you never want to use that command.

Compare this to the output of 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

And if we instead send the output of openssl dgst -sign to base64 :

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

Which should be the same value you get when you use SHA256 signatures with that key in .NET.

Solved problem simply by using OpenSSL in windows (Downloaded and build binaries with help from this tutorial ). Only difference is that you have to pass password file and cannot read output from command line (command line output do not mach file output and is incorect). My code is below:

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM