从 Java 到 C# 的 RSA 签名端口
RSA Signature port from Java to C#
我在 Java 中有这个方法,在 C# 中是相同的代码。我正在为执行此操作的 C# 代码而苦苦挣扎
private String signSHA256RSA(String input) throws Exception
{
byte[] b1 = Base64.getDecoder().decode(privKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(kf.generatePrivate(spec));
privateSignature.update(input.getBytes(StandardCharsets.UTF_8));
return byteArrayToHex(privateSignature.sign());
}
使用 .NET 标准 2.1,您需要一个小帮手来从 Base64 编码的 DER 文件中解码私钥。
using System;
using System.Security.Cryptography;
using System.Buffers.Binary;
using System.Text;
namespace Demo
{
class Program
{
const string DerPrivateKey = "MIIBywIBAAJhALkPdKoBJJg8t0Qg7VhyomS+PpKNMzn0NQ/P3zt55uAmLKenUV9xbMhW1SQRUbTEDdDUlfIiBMCzNAxB5od2IrhP4+/nKmUNsIoxOdwL0j//X74xalv9137T+y4ubLzVhwIDAQABAmAScdvq5dpD4ilR/QYq/qH48I1EBhbI+/Id9VYGk4vTY3qn6yFNJfz1qtHrml5OagvbBQLyPwjwxSumkzGelauqr4NvpOirK18v3xzhlsSmys6JZ5nILG16JByXxJjvziECMQDluHUNOCElxIbIrFOTVBaiqs4Iw9b/UJ7Wf7GglFk4pS00wjSnuDMDqGjyb4tgQ3UCMQDOOxdcSs2CPLrppT463NMqDoGide33X4s5y67E9v44IMTKuOwIXoDzgTcoGmeJwYsCMQDWUZRrA93xFXxGRngmsMH5e2+Dv+qbAsVeC35V+XGQJpKZcUKc4348wGdBIA4hfm0CMQCllxDkzDNDFZxHKqVTAiiTpl40olhWvmK+H2vPPztUugsJc34iIi+MVf6BtuHX3I0CMEDuG1uewuwcgHxWTGMnvSqQjkwtTUI0It6c8PTf8URGtoHx7HNl/wKoGGgXLTRY1w==";
static void Main(string[] args)
{
var signature = SignSHA256RSA(base64Key: DerPrivateKey, input: "Hello world!");
System.Console.WriteLine("Signature (HEX): " + signature);
}
private static string SignSHA256RSA(string base64Key, string input)
{
// Load key with the little DER helper
var rsa = LoadRSAKey(derPrivateKey: Convert.FromBase64String(DerPrivateKey));
// Sign
var inputBytes = Encoding.UTF8.GetBytes(input);
var signatureBytes = rsa.SignData(inputBytes, 0, inputBytes.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Convert signature to hex
var signatureHex = BitConverter.ToString(signatureBytes).Replace("-", string.Empty);
return signatureHex;
}
private static RSA LoadRSAKey(ReadOnlySpan<byte> derPrivateKey)
{
// Expect sequence
if (AsnHelper.GetTag(derPrivateKey[0]) != 16)
throw new FormatException($"unexpected tag");
// Read sequence length
derPrivateKey = derPrivateKey.Slice(1);
if (!AsnHelper.TryReadLength(derPrivateKey, out var length, out var bytesRead))
throw new FormatException($"unexpected length");
var sequence = AsnHelper.ReadSequence(derPrivateKey.Slice(bytesRead, length));
// https://www.hanselman.com/blog/DecodingAnSSHKeyFromPEMToBASE64ToHEXToASN1ToPrimeDecimalNumbers.aspx
var rsaParameters = new RSAParameters
{
Modulus = sequence[1].RawData,
Exponent = sequence[2].RawData,
D = sequence[3].RawData,
P = sequence[4].RawData,
Q = sequence[5].RawData,
DP = sequence[6].RawData,
DQ = sequence[7].RawData,
InverseQ = sequence[8].RawData,
};
var rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
return rsa;
}
}
/// <summary>
/// Quick helper: only works on specific key format
///
/// https://en.wikipedia.org/wiki/X.690#BER_encoding
/// </summary>
internal static class AsnHelper
{
public static int GetTag(byte value) => value & 0b11111;
public static AsnEncodedDataCollection ReadSequence(ReadOnlySpan<byte> source)
{
var sequence = new AsnEncodedDataCollection();
while (!source.IsEmpty)
{
var tag = GetTag(source[0]);
if (tag != 2)
throw new FormatException("only support integer");
source = source.Slice(1);
if (!TryReadLength(source, out var length, out var bytesRead))
throw new FormatException("invalid length");
source = source.Slice(bytesRead);
var value = new AsnEncodedData(source.Slice(0, length).ToArray());
source = source.Slice(length);
sequence.Add(value);
}
return sequence;
}
public static bool TryReadLength(ReadOnlySpan<byte> source, out int length, out int bytesRead)
{
length = 0;
bytesRead = 0;
const byte MultiByteMarker = 0x80;
bytesRead = 1;
if ((source[0] & MultiByteMarker) == 0)
{
length = source[0];
return true;
}
int lengthLength = source[0] & 0x7F;
bytesRead += lengthLength;
if (lengthLength == 2)
{
length = BinaryPrimitives.ReadInt16BigEndian(source.Slice(1));
return true;
}
return false;
}
}
}
为了测试,我使用以下内容生成了一个密钥:
openssl genrsa 768 | openssl rsa -outform der | base64 -w 0
最后我就是这样解决的。
- 我使用了这个第 3 方库 https://github.com/huysentruitw/pem-utils
然后我就用了这段代码
private static string SignSHA256RSA(string itemToSign)
{
var bytes = Encoding.UTF8.GetBytes(itemToSign);
using (var stream = File.OpenRead(@"C:\PrivateKey.pem"))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
byte[] hv = SHA256.Create().ComputeHash(bytes);
RSACryptoServiceProvider prov = new RSACryptoServiceProvider();
RSAParameters rsp = new RSAParameters();
prov.ImportParameters(rsaParameters);
RSAPKCS1SignatureFormatter rf = new RSAPKCS1SignatureFormatter(prov);
rf.SetHashAlgorithm("SHA256");
byte[] signature = rf.CreateSignature(hv);
var finalHex = BitConverter.ToString(signature).Replace("-", string.Empty).ToLowerInvariant();
return finalHex;
}
}
C#.Net代码框架:4.6,
它对我有用。
private string SignSHA256RSA(string itemToSign)
{
string filePath = Server.MapPath("Merchant_private_key_test.pem");
var bytes = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(itemToSign);
var sha256 = new SHA256CryptoServiceProvider();
byte[] rgbHash = sha256.ComputeHash(bytes);
StreamReader sr = new StreamReader(filePath);
PemReader pr = new PemReader(sr);
RsaPrivateCrtKeyParameters KeyPair = (RsaPrivateCrtKeyParameters)pr.ReadObject();
RSA rsa = DotNetUtilities.ToRSA(KeyPair);
string xmlRsa = rsa.ToXmlString(true);
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(xmlRsa);
RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
formatter.SetHashAlgorithm("SHA256");
byte[] inArray = formatter.CreateSignature(rgbHash);
return Convert.ToBase64String(inArray);
}
我在 Java 中有这个方法,在 C# 中是相同的代码。我正在为执行此操作的 C# 代码而苦苦挣扎
private String signSHA256RSA(String input) throws Exception
{
byte[] b1 = Base64.getDecoder().decode(privKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(kf.generatePrivate(spec));
privateSignature.update(input.getBytes(StandardCharsets.UTF_8));
return byteArrayToHex(privateSignature.sign());
}
使用 .NET 标准 2.1,您需要一个小帮手来从 Base64 编码的 DER 文件中解码私钥。
using System;
using System.Security.Cryptography;
using System.Buffers.Binary;
using System.Text;
namespace Demo
{
class Program
{
const string DerPrivateKey = "MIIBywIBAAJhALkPdKoBJJg8t0Qg7VhyomS+PpKNMzn0NQ/P3zt55uAmLKenUV9xbMhW1SQRUbTEDdDUlfIiBMCzNAxB5od2IrhP4+/nKmUNsIoxOdwL0j//X74xalv9137T+y4ubLzVhwIDAQABAmAScdvq5dpD4ilR/QYq/qH48I1EBhbI+/Id9VYGk4vTY3qn6yFNJfz1qtHrml5OagvbBQLyPwjwxSumkzGelauqr4NvpOirK18v3xzhlsSmys6JZ5nILG16JByXxJjvziECMQDluHUNOCElxIbIrFOTVBaiqs4Iw9b/UJ7Wf7GglFk4pS00wjSnuDMDqGjyb4tgQ3UCMQDOOxdcSs2CPLrppT463NMqDoGide33X4s5y67E9v44IMTKuOwIXoDzgTcoGmeJwYsCMQDWUZRrA93xFXxGRngmsMH5e2+Dv+qbAsVeC35V+XGQJpKZcUKc4348wGdBIA4hfm0CMQCllxDkzDNDFZxHKqVTAiiTpl40olhWvmK+H2vPPztUugsJc34iIi+MVf6BtuHX3I0CMEDuG1uewuwcgHxWTGMnvSqQjkwtTUI0It6c8PTf8URGtoHx7HNl/wKoGGgXLTRY1w==";
static void Main(string[] args)
{
var signature = SignSHA256RSA(base64Key: DerPrivateKey, input: "Hello world!");
System.Console.WriteLine("Signature (HEX): " + signature);
}
private static string SignSHA256RSA(string base64Key, string input)
{
// Load key with the little DER helper
var rsa = LoadRSAKey(derPrivateKey: Convert.FromBase64String(DerPrivateKey));
// Sign
var inputBytes = Encoding.UTF8.GetBytes(input);
var signatureBytes = rsa.SignData(inputBytes, 0, inputBytes.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Convert signature to hex
var signatureHex = BitConverter.ToString(signatureBytes).Replace("-", string.Empty);
return signatureHex;
}
private static RSA LoadRSAKey(ReadOnlySpan<byte> derPrivateKey)
{
// Expect sequence
if (AsnHelper.GetTag(derPrivateKey[0]) != 16)
throw new FormatException($"unexpected tag");
// Read sequence length
derPrivateKey = derPrivateKey.Slice(1);
if (!AsnHelper.TryReadLength(derPrivateKey, out var length, out var bytesRead))
throw new FormatException($"unexpected length");
var sequence = AsnHelper.ReadSequence(derPrivateKey.Slice(bytesRead, length));
// https://www.hanselman.com/blog/DecodingAnSSHKeyFromPEMToBASE64ToHEXToASN1ToPrimeDecimalNumbers.aspx
var rsaParameters = new RSAParameters
{
Modulus = sequence[1].RawData,
Exponent = sequence[2].RawData,
D = sequence[3].RawData,
P = sequence[4].RawData,
Q = sequence[5].RawData,
DP = sequence[6].RawData,
DQ = sequence[7].RawData,
InverseQ = sequence[8].RawData,
};
var rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
return rsa;
}
}
/// <summary>
/// Quick helper: only works on specific key format
///
/// https://en.wikipedia.org/wiki/X.690#BER_encoding
/// </summary>
internal static class AsnHelper
{
public static int GetTag(byte value) => value & 0b11111;
public static AsnEncodedDataCollection ReadSequence(ReadOnlySpan<byte> source)
{
var sequence = new AsnEncodedDataCollection();
while (!source.IsEmpty)
{
var tag = GetTag(source[0]);
if (tag != 2)
throw new FormatException("only support integer");
source = source.Slice(1);
if (!TryReadLength(source, out var length, out var bytesRead))
throw new FormatException("invalid length");
source = source.Slice(bytesRead);
var value = new AsnEncodedData(source.Slice(0, length).ToArray());
source = source.Slice(length);
sequence.Add(value);
}
return sequence;
}
public static bool TryReadLength(ReadOnlySpan<byte> source, out int length, out int bytesRead)
{
length = 0;
bytesRead = 0;
const byte MultiByteMarker = 0x80;
bytesRead = 1;
if ((source[0] & MultiByteMarker) == 0)
{
length = source[0];
return true;
}
int lengthLength = source[0] & 0x7F;
bytesRead += lengthLength;
if (lengthLength == 2)
{
length = BinaryPrimitives.ReadInt16BigEndian(source.Slice(1));
return true;
}
return false;
}
}
}
为了测试,我使用以下内容生成了一个密钥:
openssl genrsa 768 | openssl rsa -outform der | base64 -w 0
最后我就是这样解决的。
- 我使用了这个第 3 方库 https://github.com/huysentruitw/pem-utils 然后我就用了这段代码
private static string SignSHA256RSA(string itemToSign)
{
var bytes = Encoding.UTF8.GetBytes(itemToSign);
using (var stream = File.OpenRead(@"C:\PrivateKey.pem"))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
byte[] hv = SHA256.Create().ComputeHash(bytes);
RSACryptoServiceProvider prov = new RSACryptoServiceProvider();
RSAParameters rsp = new RSAParameters();
prov.ImportParameters(rsaParameters);
RSAPKCS1SignatureFormatter rf = new RSAPKCS1SignatureFormatter(prov);
rf.SetHashAlgorithm("SHA256");
byte[] signature = rf.CreateSignature(hv);
var finalHex = BitConverter.ToString(signature).Replace("-", string.Empty).ToLowerInvariant();
return finalHex;
}
}
C#.Net代码框架:4.6, 它对我有用。
private string SignSHA256RSA(string itemToSign)
{
string filePath = Server.MapPath("Merchant_private_key_test.pem");
var bytes = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(itemToSign);
var sha256 = new SHA256CryptoServiceProvider();
byte[] rgbHash = sha256.ComputeHash(bytes);
StreamReader sr = new StreamReader(filePath);
PemReader pr = new PemReader(sr);
RsaPrivateCrtKeyParameters KeyPair = (RsaPrivateCrtKeyParameters)pr.ReadObject();
RSA rsa = DotNetUtilities.ToRSA(KeyPair);
string xmlRsa = rsa.ToXmlString(true);
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(xmlRsa);
RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
formatter.SetHashAlgorithm("SHA256");
byte[] inArray = formatter.CreateSignature(rgbHash);
return Convert.ToBase64String(inArray);
}