我今天来找你,在我的头撞在加密的砖墙上之后,我手里拿着帽子。
这是前提:我有一个生成 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 的(基本)理解告诉我它应该是一个全有或全无的函数——整个文本被解密,或者我得到胡言乱语。