1

我今天来找你,在我的头撞在加密的砖墙上之后,我手里拿着帽子。

这是前提:我有一个生成 ASN.1 编码的 DER 密钥的 python 脚本。然后我有另一个 python 脚本,它获取那个 DER 私钥并使用它来解密一个 base64 编码的字符串。我目前正在尝试将第二个脚本转换为它的 C# 替代方案,并且我已经让它工作了——在一定程度上。

使用原始 python 脚本时,解密后的输出为 127 字节长,而我拥有的 C# 代码为 16 字节长。在这一点上,我认为我的 C# 代码完全错误,直到我注意到 python 输出和 C# 输出的最后 16 个字节完全相同。笏?

这是我的prosterity代码(obv没有错误检查等)

输入 DER 私钥(用于 Web 的 Base64 编码)

MIICXAIBAAKBgQDMJQczcaoGepZIPcTGJB5AfSBFqhCt5B+HRpaUosZzAhooljxKrlRd3oLww2pqyjyGl9bqrKhaRuCPaEg/RHYvqT3gEjtW9XrF3absMP2ihp+SgOTnRaQLlK7Wyh2QU4XKTd0Pyjv+nYTM3R1LRveN2YEg+dWUeGqGJuNs9NArqwIDAQABAoGBAMT0kI78gbrAeM938Knt3NYBIqqzmmX6qsR7wPnkVaxOAejYkZDHwpPSAujA0KH5Pg3o3qwJKl/2897IELQhnBdCfb1AvL87ENj0Kh1+SP5Y2LeqQVRE3HT1fGXlbBDdXDiCNiBcgnIT7nsVKA+KmKUjJI/MvefM9p2sVJudVfdxAkEA+6GtJqjOF4Ver/O+J8LJF5O0/fh6MRvdHKqPNehm+NygtAxsygu1MTpZFJVAHsfdQnL+rRJi9vbqiwHfNbsVmQJBAM+wTmCHhJ7g3RAGswhbgAqg3LfM8dre03b4fwHkp4+S6j5tcYazrN8dqifiL83Hyo0522BQsRt+YADIk7f1TeMCQFhEXWW7Pxf3G8Di4mg2Jq4TjSCtocdKO+TLW5MQY9aWJfUiiqLROoz7J7ZVqHljqJSfnAB/+6Ef+iQq0u6ZIrkCQGDELZBuM81uybD43hurvjm1f4EnvRwULATHfS2dorCTbA6QIY/4UThXcvtIOKuxRd+NMHhswEgmFobm7WSNp68CQEMn75sLC874hitiqVVu6bNyDsOG4X6Cyc8uLKA58BeIg8eG590ehrFHYR0JsawgePsAVXb/RCPuYgONi5TRHBk=

输入加密字符串(Base64 编码)

e1algxNK5vfiLQmN42bQf9CHJnRGH06w13P+ObHx5U7XJWbCsh9HKclXX88b2peEG4U3K4WC+dSNGLEPe8d3bPwxlBOYXVgsAHKLrgD7gXJDOG+gMawUsUlVx+hWPESITHXDscbcM6zASUuIWGtPkJw3r00MwJy9ZzYqfr2OiJg=

Python RSA 脚本使用 (PyCrypt) 解码加密字符串

//removed python code to convert base64 encoded DER key to ASN.1 and then to RSA key tuple for pycrypto, the following is the hex data within the tuple:
key = ['0x0:0xcc:0x25:0x7:0x33:0x71:0xaa:0x6:0x7a:0x96:0x48:0x3d:0xc4:0xc6:0x24:0x1e:0x40:0x7d:0x20:0x45:0xaa:0x10:0xad:0xe4:0x1f:0x87:0x46:0x96:0x94:0xa2:0xc6:0x73:0x2:0x1a:0x28:0x96:0x3c:0x4a:0xae:0x54:0x5d:0xde:0x82:0xf0:0xc3:0x6a:0x6a:0xca:0x3c:0x86:0x97:0xd6:0xea:0xac:0xa8:0x5a:0x46:0xe0:0x8f:0x68:0x48:0x3f:0x44:0x76:0x2f:0xa9:0x3d:0xe0:0x12:0x3b:0x56:0xf5:0x7a:0xc5:0xdd:0xa6:0xec:0x30:0xfd:0xa2:0x86:0x9f:0x92:0x80:0xe4:0xe7:0x45:0xa4:0xb:0x94:0xae:0xd6:0xca:0x1d:0x90:0x53:0x85:0xca:0x4d:0xdd:0xf:0xca:0x3b:0xfe:0x9d:0x84:0xcc:0xdd:0x1d:0x4b:0x46:0xf7:0x8d:0xd9:0x81:0x20:0xf9:0xd5:0x94:0x78:0x6a:0x86:0x26:0xe3:0x6c:0xf4:0xd0:0x2b:0xab',
'0x1:0x0:0x1',
'0x0:0xc4:0xf4:0x90:0x8e:0xfc:0x81:0xba:0xc0:0x78:0xcf:0x77:0xf0:0xa9:0xed:0xdc:0xd6:0x1:0x22:0xaa:0xb3:0x9a:0x65:0xfa:0xaa:0xc4:0x7b:0xc0:0xf9:0xe4:0x55:0xac:0x4e:0x1:0xe8:0xd8:0x91:0x90:0xc7:0xc2:0x93:0xd2:0x2:0xe8:0xc0:0xd0:0xa1:0xf9:0x3e:0xd:0xe8:0xde:0xac:0x9:0x2a:0x5f:0xf6:0xf3:0xde:0xc8:0x10:0xb4:0x21:0x9c:0x17:0x42:0x7d:0xbd:0x40:0xbc:0xbf:0x3b:0x10:0xd8:0xf4:0x2a:0x1d:0x7e:0x48:0xfe:0x58:0xd8:0xb7:0xaa:0x41:0x54:0x44:0xdc:0x74:0xf5:0x7c:0x65:0xe5:0x6c:0x10:0xdd:0x5c:0x38:0x82:0x36:0x20:0x5c:0x82:0x72:0x13:0xee:0x7b:0x15:0x28:0xf:0x8a:0x98:0xa5:0x23:0x24:0x8f:0xcc:0xbd:0xe7:0xcc:0xf6:0x9d:0xac:0x54:0x9b:0x9d:0x55:0xf7:0x71']

// from https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA-module.html
// from Crypto.PublicKey import RSA

rsa = RSA.construct(key)
plain_text = RSA.decrypt(cipher_text.decode('base64'))

Python 脚本输出

0x02:0x96:0xd3:0x80:0xeb:0xcc:0x89:0xa1:0xff:0x0f:0x97:0x64:0x21:0x6b:0xf1:0x69:0xe3:0xa9:0xb7:0x5a:0x7e:0xd8:0xe8:0x2d:0xa9:0x27:0x78:0x90:0x8a:0x56:0x58:0xcd:0x4a:0x08:0x24:0x22:0xdd:0x88:0xa0:0x58:0xe1:0x18:0xd2:0xe4:0xca:0xa3:0xba:0x70:0xa7:0x6d:0x07:0x40:0x9e:0x2a:0x23:0x05:0x73:0x5e:0x23:0xb3:0x0c:0xa8:0xb4:0x43:0xf8:0xee:0xa6:0x67:0xfb:0x56:0xb0:0xcc:0x59:0xe3:0x59:0x9b:0xe5:0x9c:0xae:0xca:0x3f:0x17:0x73:0xf8:0x69:0x1b:0x73:0x20:0xfe:0x2a:0x0d:0x91:0x8f:0x94:0x26:0xde:0x3c:0xdb:0xa2:0x3e:0xf9:0x8f:0x0d:0x8c:0xd2:0x7b:0xfb:0xd6:0x92:0x8c:0x00:0x11:0x5c:0x31:0x5e:0xee:0x76:0xfe:0xf5:0x4a:0x9e:0x04:0x51:0x8a:0x3d:0x93:0xec

C# RSA 解密脚本

static void Main(string[] args)
{
    var derContent = Convert.FromBase64String(base64DerContent); //base64DerContent is the "Input DER Private Key" above
    var rsa = DecodeRSAPrivateKey(derContent);
    Byte[] cipher_text_data = Convert.FromBase64String("e1algxNK5vfiLQmN42bQf9CHJnRGH06w13P+ObHx5U7XJWbCsh9HKclXX88b2peEG4U3K4WC+dSNGLEPe8d3bPwxlBOYXVgsAHKLrgD7gXJDOG+gMawUsUlVx+hWPESITHXDscbcM6zASUuIWGtPkJw3r00MwJy9ZzYqfr2OiJg=");

    Byte[] raw = rsa.Decrypt(cipher_text_data, false);
        string hex = BitConverter.ToString(raw);
        System.Console.WriteLine("Decrypted: " + hex);
}
//the following code taken from http://www.jensign.com/opensslkey/opensslkey.cs
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider  ---
    public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
    {
        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);

            System.Console.WriteLine("showing components ..");
            if (true)
            {
                System.Console.WriteLine("\nModulus", MODULUS);
                System.Console.WriteLine("\nExponent", E);
                System.Console.WriteLine("\nD", D);
                System.Console.WriteLine("\nP", P);
                System.Console.WriteLine("\nQ", Q);
                System.Console.WriteLine("\nDP", DP);
                System.Console.WriteLine("\nDQ", DQ);
                System.Console.WriteLine("\nIQ", IQ);
            }

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

C# 脚本输出

11-5C-31-5E-EE-76-FE-F5-4A-9E-04-51-8A-3D-93-EC

如您所见,C# 代码的输出只有 16 个字节长,但它直接匹配 Python 脚本的最后 16 个字节。我不确定到底发生了什么,我对 RSA 的(基本)理解告诉我它应该是一个全有或全无的函数——整个文本被解密,或者我得到胡言乱语。

4

1 回答 1

3

这里的问题是填充。RSA 操作要求在加密之前使用安全填充方案填充消息。否则,存在可以恢复私钥的某些攻击。这是一篇很好的文章,解释了原因

出于某种原因,Python 加密库不会为您删除填充,但 C# 库会。因此,您必须在 Python 代码中手动删除填充。

幸运的是,从您提供的数据来看,这里使用的填充方案似乎是 PKCS#1.5。这是一个相对简单的填充方案,很容易处理。完整的规范在 RFC 3447 中,但归结为:

EM = 0x00 || 0x02 || PS || 0x00 || M.

其中EM是加密前的填充消息,PS是填充,M是原始的未填充消息。

这是一个应该为您完成工作的 Python 函数:

def RemovePKCS15Padding( padded_msg ):
    if len(padded_msg) < 2 or padded_msg[0]!='\x02':
        raise PaddingError # or whatever
    p = padded_msg.find('\x00')
    if p < 0:
        raise PaddingError # or whatever
    return padded_msg[p+1:]
于 2013-08-20T18:17:59.493 回答