首页新闻找找看学习计划

C#如何通过已有私钥进行RSA解密

2
悬赏园豆:200 [已解决问题] 解决于 2015-03-31 18:46

私钥如下:

-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCf1a4LQyipBqeUCZ9kKsfasQzkEFCBmGsM21Sakb5BO0sY07GD
cproJHF2xNQrV0cM7+liE3pBUFsarui2WaHZhAibpLbl9z4FSfoN5hSg6sEgbB17
SvKe3ZN/75GoEsQiQtYW4gUJgzrBovVZ+TeTnN+NHHBqUqBKhNIgPFVapQIDAQAB
AoGAG0OMs5kaF3LuJN9bU+/ENXab908dHG4OXJwRG2ie5muhzLNXhU+IQu7sd9Dt
TBNQKFHIIpWl9fwp/iw1v90cMUQGj0zhSXHAz7Vak/ryQLTyeIIciL8MQWvnbAaN
lIoFq2wBl7SYs3n71B4MlvvTysaG0krsjiPh5LVgnBvzjGECQQDcAwe4XnF7SHWO
nfljrG29soKNiUhYKtDGcV9fvam9u50Ek882wvFmsJP+tk+1CXjMRSNlOi40bxKC
uaBa1JOtAkEAufq9FmZHfBFf3e6n57wLiAj5C1MeyHAtt6qdAF49OZJBGZh1pePn
jDGNezFvy7U5bMp7/updisLCFueS5eKB2QJAF84QIMe/OZqedZ7sI/e9LABLlerb
tAZ17nLH4gEQg6HwHFWt3vv6yKSkbrPlLe5nbpqweLxx0WSPOSvCiPFlRQJAPAfF
NQ+6jz+EdDxukgxOpJBQ4ujnjMc42ooFt3KzzHt66+ocP3m66bOs+VDRxy0t5gHN
2FCJ9Ro8T+xbrDxasQJAARHpcG6tE0F+lmUthtep1U8OrF+AQvqDhBq8MYK+/pF/
LRZkFHkqTsj89OyWDlSH3LeYkOWsr9mAFxsvHZ9BSA==
-----END RSA PRIVATE KEY-----

在C#中进行RSA解密,需要用RSACryptoServiceProvider。但它只有一个.FromXmlString()方法支持通过字符串设置私钥,该方法需要指定格式的私钥字,上面的字符私钥字符串无法使用。

网上也有人遇到了同样的问题(RSA decryption in C# w/ preexisting private key?),至今未解决。

请问如何解决这个问题?

为了方便测试,提供一下对应的公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf1a4LQyipBqeUCZ9kKsfasQzk
EFCBmGsM21Sakb5BO0sY07GDcproJHF2xNQrV0cM7+liE3pBUFsarui2WaHZhAib
pLbl9z4FSfoN5hSg6sEgbB17SvKe3ZN/75GoEsQiQtYW4gUJgzrBovVZ+TeTnN+N
HHBqUqBKhNIgPFVapQIDAQAB
-----END PUBLIC KEY-----
C#
dudu的主页 dudu | 高人七级 | 园豆:38648
提问于:2015-03-31 14:52
< >
分享
最佳答案
1

你的是pem key,需要下转换为c# rsa provider认识的key。

首先,干掉头部和尾部的无用字符,然后利用如下方法转换:

[TestMethod]
        public void TestMethod4()
        {
            string priKey = @"-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCf1a4LQyipBqeUCZ9kKsfasQzkEFCBmGsM21Sakb5BO0sY07GD
cproJHF2xNQrV0cM7+liE3pBUFsarui2WaHZhAibpLbl9z4FSfoN5hSg6sEgbB17
SvKe3ZN/75GoEsQiQtYW4gUJgzrBovVZ+TeTnN+NHHBqUqBKhNIgPFVapQIDAQAB
AoGAG0OMs5kaF3LuJN9bU+/ENXab908dHG4OXJwRG2ie5muhzLNXhU+IQu7sd9Dt
TBNQKFHIIpWl9fwp/iw1v90cMUQGj0zhSXHAz7Vak/ryQLTyeIIciL8MQWvnbAaN
lIoFq2wBl7SYs3n71B4MlvvTysaG0krsjiPh5LVgnBvzjGECQQDcAwe4XnF7SHWO
nfljrG29soKNiUhYKtDGcV9fvam9u50Ek882wvFmsJP+tk+1CXjMRSNlOi40bxKC
uaBa1JOtAkEAufq9FmZHfBFf3e6n57wLiAj5C1MeyHAtt6qdAF49OZJBGZh1pePn
jDGNezFvy7U5bMp7/updisLCFueS5eKB2QJAF84QIMe/OZqedZ7sI/e9LABLlerb
tAZ17nLH4gEQg6HwHFWt3vv6yKSkbrPlLe5nbpqweLxx0WSPOSvCiPFlRQJAPAfF
NQ+6jz+EdDxukgxOpJBQ4ujnjMc42ooFt3KzzHt66+ocP3m66bOs+VDRxy0t5gHN
2FCJ9Ro8T+xbrDxasQJAARHpcG6tE0F+lmUthtep1U8OrF+AQvqDhBq8MYK+/pF/
LRZkFHkqTsj89OyWDlSH3LeYkOWsr9mAFxsvHZ9BSA==
-----END RSA PRIVATE KEY-----";

            priKey = priKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .Replace("-----END RSA PRIVATE KEY-----", "");

            var rsaProvider = DecodeRSAPrivateKey(priKey);
        }

        public static RSACryptoServiceProvider DecodeRSAPrivateKey(string priKey)
        {
            var privkey = Convert.FromBase64String(priKey);
            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
            MemoryStream mem = new MemoryStream(privkey);
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
            byte bt = 0;
            ushort twobytes = 0;
            int elems = 0;
            try
            {
                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                    binr.ReadByte();        //advance 1 byte
                else if (twobytes == 0x8230)
                    binr.ReadInt16();       //advance 2 bytes
                else
                    return null;

                twobytes = binr.ReadUInt16();
                if (twobytes != 0x0102) //version number
                    return null;
                bt = binr.ReadByte();
                if (bt != 0x00)
                    return null;


                //------  all private key components are Integer sequences ----
                elems = GetIntegerSize(binr);
                MODULUS = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                E = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                D = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                P = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                Q = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                DP = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                DQ = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                IQ = binr.ReadBytes(elems);

                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                RSAParameters RSAparams = new RSAParameters();
                RSAparams.Modulus = MODULUS;
                RSAparams.Exponent = E;
                RSAparams.D = D;
                RSAparams.P = P;
                RSAparams.Q = Q;
                RSAparams.DP = DP;
                RSAparams.DQ = DQ;
                RSAparams.InverseQ = IQ;
                RSA.ImportParameters(RSAparams);
                return RSA;
            }
            catch (Exception)
            {
                return null;
            }
            finally
            {
                binr.Close();
            }
        }

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

            if (bt == 0x81)
                count = binr.ReadByte();    // data size in next byte
            else
                if (bt == 0x82)
                {
                    highbyte = binr.ReadByte();    // data size in next 2 bytes
                    lowbyte = binr.ReadByte();
                    byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                    count = BitConverter.ToInt32(modint, 0);
                }
                else
                {
                    count = bt;        // we already have the data size
                }

            while (binr.ReadByte() == 0x00)
            {    //remove high order zeros in data
                count -= 1;
            }
            binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte
            return count;
        }
    }

能不能成功就靠你测试了。

 

参考: 

http://stackoverflow.com/questions/243646/how-to-read-a-pem-rsa-private-key-from-net 

http://www.jensign.com/opensslkey/opensslkey.cs

收获园豆:200
幻天芒 | 高人七级 |园豆:36544 | 2015-03-31 15:32

测试没成功,第1个 twobytes = binr.ReadUInt16(); 读出来的是值是0x494D

dudu | 园豆:38648 (高人七级) | 2015-03-31 16:56

@幻天芒: 测试成功!这个方法可行!

dudu | 园豆:38648 (高人七级) | 2015-03-31 18:40

最终采用的是来自 https://gist.github.com/canton7/5670788 的代码:

private static RSACryptoServiceProvider DecodeRSAPrivateKey(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()");

        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 static 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;
}
dudu | 园豆:38648 (高人七级) | 2015-03-31 18:46

@dudu: 我也长见识了,没想到rsa还有这个样子的密钥。:)

幻天芒 | 园豆:36544 (高人七级) | 2015-04-01 08:59

@幻天芒: openssl生成的密钥就是这样的

dudu | 园豆:38648 (高人七级) | 2015-04-01 09:04

@dudu: 学习了,以后遇到了也有方法解密了。

幻天芒 | 园豆:36544 (高人七级) | 2015-04-01 09:05
其他回答(8)
0

分多任性!路过求打赏..

丫的 | 园豆:1575 (小虾三级) | 2015-03-31 15:00
0

你去CSDN搜“代码小兵的成长”,当然就是哥自己啦,我以前贴过代码,你看看你需要不

代码小兵的成长 | 园豆:301 (菜鸟二级) | 2015-03-31 15:03
0

你这是用 OpenSSL 导出的吧,你可以再用 OpenSSL 读入.

翻了下,以前回答过:http://q.cnblogs.com/q/63278/

Launcher | 园豆:45040 (高人七级) | 2015-03-31 15:04
0

几万的园豆还互相给。。。。太讨厌啦

羽商宫 | 园豆:2493 (老鸟四级) | 2015-03-31 15:27
0

我帮忙顶。没接触过这块。不了解

小二炒豆芽菜 | 园豆:418 (菜鸟二级) | 2015-03-31 16:00
0

好东西,关注下!

519740105 | 园豆:5810 (大侠五级) | 2015-03-31 16:48
0

mark

花儿笑弯了腰 | 园豆:264 (菜鸟二级) | 2015-04-01 08:36
0

楼主我也遇到和你一样的问题,但是我用上面代码测试,twobytes = binr.ReadUInt16();这个里面的值是一个数字,下面的判断都没用,请问楼主是怎么解决的?

y_kami | 园豆:159 (初学一级) | 2017-04-06 13:40
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册