C# 如何使用rsa-sha256通过私钥对字符串进行签名?
我必须在每次调用C# 如何使用rsa-sha256通过私钥对字符串进行签名?,c#,rsa,C#,Rsa,我必须在每次调用API时使用OpenSSL生成的privateKey对字符串进行签名,然后从数据库接收privateKey,并为每个用户进行更改 我已经读到我应该使用rsacyptoservice并添加privateKey作为参数来实现这一点。但是,我在ImportParameters 代码如下: string privateKey = "-----BEGIN RSA PRIVATE KEY----- MIIJ....." RSAParameters rsap =
API
时使用OpenSSL生成的privateKey
对字符串进行签名,然后从数据库接收privateKey
,并为每个用户进行更改
我已经读到我应该使用rsacyptoservice
并添加privateKey
作为参数来实现这一点。但是,我在ImportParameters
代码如下:
string privateKey = "-----BEGIN RSA PRIVATE KEY-----
MIIJ....."
RSAParameters rsap = new RSAParameters
{
Modulus = Encoding.ASCII.GetBytes(privateKey)
};
rsa.ImportParameters(rsap);
byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(StringToSign), false);
string base64Encrypted = Convert.ToBase64String(encryptedData);
在服务文档中指出,需要使用RSA-SHA256对字符串进行签名。下面是在Node.JS中为该字符串签名的代码:
const signature = crypto.createSign('RSA-SHA256').update(string).sign(privateKey, 'base64')
但是,我在c#中找不到任何类似的代码。问题在于此代码:
RSAParameters rsap = new RSAParameters
{
Modulus = Encoding.ASCII.GetBytes(privateKey)
};
您正在以字符串编码的字节数组形式读取privateKey。但是您的私钥不是那种格式,而是PEM格式。请检查以下答案:。用于在C#中读取PEM密钥。也许你也应该解密pem字符串中的base64。在@SmileDeveloper链接的答案中,链接了
OpenSSLKey
的源代码,通过重用源代码中链接的一些函数,我使用我的pemprivateKey
作为字符串对另一个字符串进行了签名
用于执行此操作的代码如下所示:
// encoding my privateKey from string to byte[] by using DecodeOpenSSLPrivateKey function from OpenSSLKey source code
byte[] pemprivatekey = DecodeOpenSSLPrivateKey(privateKey);
// enconding my string to sign in byte[]
byte[] byteSign = Encoding.ASCII.GetBytes(Sign);
// using DecodeRSAPrivateKey function from OpenSSLKey source code to get the RSACryptoServiceProvider with all needed parameters
var rsa = DecodeRSAPrivateKey(pemprivatekey);
// Signing my string with previously get RSACryptoServiceProvider in SHA256
var byteRSA = rsa.SignData(byteSign, CryptoConfig.MapNameToOID("SHA256"));
// As required by docs converting the signed string to base64
string Signature = Convert.ToBase64String(byteRSA);
以下是上面使用的从OpenSSLKey重用的所有函数:
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);
// ------- 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();
}
}
public static byte[] DecodeOpenSSLPrivateKey(String instr)
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter))
return null;
StringBuilder sb = new StringBuilder(pemstr);
sb.Replace(pemprivheader, ""); //remove headers/footers, if present
sb.Replace(pemprivfooter, "");
String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
binkey = Convert.FromBase64String(pvkstr);
return binkey;
}
catch (System.FormatException)
{ //if can't b64 decode, it must be an encrypted private key
//Console.WriteLine("Not an unencrypted OpenSSL PEM private key");
}
StringReader str = new StringReader(pvkstr);
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED"))
return null;
String saltline = str.ReadLine();
if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,"))
return null;
String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim();
byte[] salt = new byte[saltstr.Length / 2];
for (int i = 0; i < salt.Length; i++)
salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16);
if (!(str.ReadLine() == ""))
return null;
//------ remaining b64 data is encrypted RSA key ----
String encryptedstr = str.ReadToEnd();
try
{ //should have b64 encrypted RSA key now
binkey = Convert.FromBase64String(encryptedstr);
}
catch (System.FormatException)
{ // bad b64 data.
return null;
}
//------ Get the 3DES 24 byte key using PDK used by OpenSSL ----
SecureString despswd = GetSecPswd("Enter password to derive 3DES key==>");
//Console.Write("\nEnter password to derive 3DES key: ");
//String pswd = Console.ReadLine();
byte[] deskey = GetOpenSSL3deskey(salt, despswd, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//showBytes("3DES key", deskey) ;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
if (rsakey != null)
return rsakey; //we have a decrypted RSA private key
else
{
Console.WriteLine("Failed to decrypt RSA private key; probably wrong password.");
return null;
}
}
private static byte[] GetOpenSSL3deskey(byte[] salt, SecureString secpswd, int count, int miter)
{
IntPtr unmanagedPswd = IntPtr.Zero;
int HASHLENGTH = 16; //MD5 bytes
byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results
byte[] psbytes = new byte[secpswd.Length];
unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
//UTF8Encoding utf8 = new UTF8Encoding();
//byte[] psbytes = utf8.GetBytes(pswd);
// --- contatenate salt and pswd bytes into fixed data array ---
byte[] data00 = new byte[psbytes.Length + salt.Length];
Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes
Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes
// ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ----
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = null;
byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget
for (int j = 0; j < miter; j++)
{
// ---- Now hash consecutively for count times ------
if (j == 0)
result = data00; //initialize
else
{
Array.Copy(result, hashtarget, result.Length);
Array.Copy(data00, 0, hashtarget, result.Length, data00.Length);
result = hashtarget;
//Console.WriteLine("Updated new initial hash target:") ;
//showBytes(result) ;
}
for (int i = 0; i < count; i++)
result = md5.ComputeHash(result);
Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial
}
//showBytes("Final key material", keymaterial);
byte[] deskey = new byte[24];
Array.Copy(keymaterial, deskey, deskey.Length);
Array.Clear(psbytes, 0, psbytes.Length);
Array.Clear(data00, 0, data00.Length);
Array.Clear(result, 0, result.Length);
Array.Clear(hashtarget, 0, hashtarget.Length);
Array.Clear(keymaterial, 0, keymaterial.Length);
return deskey;
}
public static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV)
{
MemoryStream memst = new MemoryStream();
TripleDES alg = TripleDES.Create();
alg.Key = desKey;
alg.IV = IV;
try
{
CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
return null;
}
byte[] decryptedData = memst.ToArray();
return decryptedData;
}
private static SecureString GetSecPswd(String prompt)
{
SecureString password = new SecureString();
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write(prompt);
Console.ForegroundColor = ConsoleColor.Magenta;
while (true)
{
ConsoleKeyInfo cki = Console.ReadKey(true);
if (cki.Key == ConsoleKey.Enter)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (cki.Key == ConsoleKey.Backspace)
{
// remove the last asterisk from the screen...
if (password.Length > 0)
{
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
password.RemoveAt(password.Length - 1);
}
}
else if (cki.Key == ConsoleKey.Escape)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (Char.IsLetterOrDigit(cki.KeyChar) || Char.IsSymbol(cki.KeyChar))
{
if (password.Length < 20)
{
password.AppendChar(cki.KeyChar);
Console.Write("*");
}
else
{
Console.Beep();
}
}
else
{
Console.Beep();
}
}
}
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;
}
公共静态RSACryptoServiceProvider DecodeRSAPrivateKey(字节[]privkey)
{
字节[]模数,E,D,P,Q,DP,DQ,IQ;
//------设置流以解码asn.1编码的RSA私钥------
MemoryStream mem=新的MemoryStream(privkey);
BinaryReader binr=新的BinaryReader(mem);//使用BinaryReader包装内存流以便于阅读
字节bt=0;
ushort-twobytes=0;
整数=0;
尝试
{
twobytes=binr.ReadUInt16();
if(twobytes==0x8130)//数据以小尾端顺序读取(序列的实际数据顺序为3081)
binr.ReadByte();//前进1字节
else if(两个字节==0x8230)
binr.ReadInt16();//前进2字节
其他的
返回null;
twobytes=binr.ReadUInt16();
if(twobytes!=0x0102)//版本号
返回null;
bt=binr.ReadByte();
如果(bt!=0x00)
返回null;
//------所有私钥组件都是整数序列----
elems=getintegerize(binr);
模数=二进制读取字节(elems);
elems=getintegerize(binr);
E=二进制读取字节(elems);
elems=getintegerize(binr);
D=二进制读取字节(elems);
elems=getintegerize(binr);
P=二进制读取字节(elems);
elems=getintegerize(binr);
Q=二进制读取字节(elems);
elems=getintegerize(binr);
DP=二进制读取字节(elems);
elems=getintegerize(binr);
DQ=二进制读取字节(elems);
elems=getintegerize(binr);
IQ=二进制读取字节(elems);
//----创建RSACryptServiceProvider实例并使用公钥初始化-----
RSACryptoServiceProvider RSA=新的RSACryptoServiceProvider();
rsapameters rsaparms=新的rsapameters();
RSAparams.模数=模数;
rsaparms.指数=E;
RSAparams.D=D;
rsaparms.P=P;
rsaparms.Q=Q;
RSAparams.DP=DP;
RSAparams.DQ=DQ;
rsaparms.InverseQ=智商;
RSA.输入参数(rsaparms);
返回RSA;
}
捕获(例外)
{
返回null;
}
最后
{
binr.Close();
}
}
公共静态字节[]解码OpenSSLPRIVATEKEY(字符串指令)
{
const String pemprivheader=“----开始RSA私钥------”;
常量字符串pemprivfooter=“----结束RSA私钥------”;
字符串pemstr=instr.Trim();
字节[]binkey;
如果(!pemstr.StartsWith(pemprivheader)| |!pemstr.EndsWith(pemprivfooter))
返回null;
StringBuilder sb=新StringBuilder(pemstr);
sb.替换(页眉“”;//删除页眉/页脚(如果存在)
sb.替换(页脚“”);
String pvkstr=sb.ToString().Trim();//删除前导/尾随空格后获取字符串
尝试
{//如果没有PEM加密信息行,这是一个未加密的PEM私钥
binkey=Convert.FromBase64String(pvkstr);
返回binkey;
}
捕获(System.FormatException)
{//如果b64无法解码,则它必须是加密的私钥
//WriteLine(“不是未加密的OpenSSL PEM私钥”);
}
StringReader str=新的StringReader(pvkstr);
//--------阅读PEM加密信息行并提取盐-----
如果(!str.ReadLine().StartsWith(“Proc类型:4,加密”))
返回null;
字符串saltline=str.ReadLine();
如果(!saltline.StartsWith(“DEK信息:DES-EDE3-CBC,”)
返回null;
String saltstr=saltline.Substring(saltline.IndexOf(“,”)+1.Trim();
字节[]salt=新字节[saltstr.Length/2];
for(int i=0;i