我正在尝试使用 SHA256 算法和 Pkcs1 RSA 签名填充在 Java 中创建哈希的数字签名(使用 HMACSHA256 创建),但它不会生成与供应商在 .net 中实现的签名相同的签名。
我的Java代码:
String secretKey = "1n6pobYP5+BZHgEdFCtNibcb1eykysLMbWMV2fJD0OwfmY3TbtRlUaRWu8A7Fp0dt8O+TCIl3LABlUrW48FFUg==";
private String hmacSha256(String data, String key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hmacSha256Bytes = mac.doFinal(data.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(hmacSha256Bytes);
}
private String signHash(String hash, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA", "BC");
signature.initSign(privateKey);
signature.update(Base64.getDecoder().decode(hash));
byte[] signedBytes = signature.sign();
return Base64.getEncoder().encodeToString(signedBytes);
}
private String signMessage(String hashedMsg, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA", "BC");
signature.initSign(privateKey);
signature.update(hashedMsg.getBytes("UTF-8"));
byte[] signedBytes = signature.sign();
return Base64.getEncoder().encodeToString(signedBytes);
}
SignHash 是向已经散列的消息添加签名,而 SignMessage 是向已经散列的消息再次添加散列,然后添加签名。 SignMessage 正在根据供应商进行正确的签名,但 SignHash 正在创建不同的签名。
供应商对 SignHash 函数的 Dotnet 代码实现:
String secretKey = "1n6pobYP5+BZHgEdFCtNibcb1eykysLMbWMV2fJD0OwfmY3TbtRlUaRWu8A7Fp0dt8O+TCIl3LABlUrW48FFUg==";
public static string HashInstruction(string message, string key)
{
using HMACSHA256 hMACSHA = new HMACSHA256(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hMACSHA.ComputeHash(Encoding.UTF8.GetBytes(message)));
}
public static string SignHashWithFile(string message, string privateKeyFile)
{
byte[] array = ReadPrivateFile(privateKeyFile);
RSA rSA = RSA.Create();
rSA.ImportRSAPrivateKey(array, out var _);
byte[] hash = Convert.FromBase64String(message);
byte[] inArray = rSA.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(inArray);
}
public static string SignDataWithFile(string message, string privateKeyFile)
{
byte[] array = ReadPrivateFile(privateKeyFile);
RSA rSA = RSA.Create();
rSA.ImportRSAPrivateKey(array, out var _);
byte[] data = Encoding.ASCII.GetBytes(message);
byte[] inArray = rSA.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(inArray);
}
SignDataWithFile() 生成与signMessage() 相同的签名,但signHash() 和SignHashWithFile() 生成不同的签名。
.NET 的示例值:
哈希前的数据:'512|PUB55442201|3555101122526|20000'
哈希后的数据:'vGEWCW/XP7NPFIrY4sOo+jE4Q713RfVlKV/VV8ZQG6I='
签名(signhash):'m3CHGVvBy90CVVnPzzFYQAekN6oy/isfz+fAVyXsJ/COTZ/fCMemANy0Q+yWlh/WUkSGbS7SQrc/33m+Wy69XrctOYSZcWMZpGFGBwBrCkg+wmscBfYItBqKlqmbQr2W0CI xOJlj5fLTeWYQ0MLCrMoBB9g1sB93eECYy9G5MzNfrRQaHVVzQrvBdyphABf8GsIuOzfjTOca5jGCZq6+0i/Xnb49jpkr3bPG3JP2+HbwKbyeqzB59pQaQ1+ye1pE7w85b0Sd3WDSfyj mYDBcNUooaMzekROaR8GPvlerhr1LbXwzvhoDON3ZJhFjerSikcrrM1JeL+s0DOCSQdvsBg=='
签名(signData):'wLDprhCgUVG3V0pgU6DCZDQyoHf3VSQ7PMs9T6Ns3OM/lIz2cvACSVFJIs5W5sR7NN/jcp8k6yjQreq9SuSi9EPJsHFNlsqDjfqt78KOkqVD41PlFKRguPi9caMskRSKF/Ja1np XEweQw0MLgxGhs1sOffEzgk7Tqq4N3p2LN4mxgBtxoK3S+HfybPbHKpeSqx6Y0dVfz+Axa3v0/ChNU1+ZHA68H7+j7opdtaXTsx4dzodMvzdpfmFOpC9ye+mhmbJWBfhHDUd+AtIgLSCUZ0v +YhTLFlFY7hUEYRMqk3/tAxLw6dshXla9TSyzkJe3lijJabWGaMGqDgGC+/0puw=='
Java 的示例值:
哈希前的数据:'512|PUB55442201|3555101122526|20000'
哈希后的数据:'vGEWCW/XP7NPFIrY4sOo+jE4Q713RfVlKV/VV8ZQG6I='
签名(signhash):'W1U84sux5el9Tij8Dd9Tu2VbcXC2/BUwZFn5qeYh9M1D/ym6WjahyseTqUb6QFLuy1I9AAQN7p71NvYm1XpKKSGlJMl97cyHPijH3vj/+ydceLWm6Z4cIcJHrZsR ld9EYNfYIW7e3h7UVQRwexZaNS+PjcWDatuUWZevCGnQNzu20D3dIhdJdcZUp/J0rRO0rrWl9njXWDshiZz9mnVYx2wnoOJC6+2j2UBQOezupBzA5ZcvWaTrJhnes0l8aKhgwcdfb59ifuwhvcrFKB TxLkFVI7eMJslluHcFe2BYVjhmOQkqVfbkTYXgBqEsiQjCNdHlYsY15ZSbVDNpt9fthA=='
签名(signData):'wLDprhCgUVG3V0pgU6DCZDQyoHf3VSQ7PMs9T6Ns3OM/lIz2cvACSVFJIs5W5sR7NN/jcp8k6yjQreq9SuSi9EPJsHFNlsqDjfqt78KOkqVD41PlFKRguPi9caMskRSKF/Ja1np XEweQw0MLgxGhs1sOffEzgk7Tqq4N3p2LN4mxgBtxoK3S+HfybPbHKpeSqx6Y0dVfz+Axa3v0/ChNU1+ZHA68H7+j7opdtaXTsx4dzodMvzdpfmFOpC9ye+mhmbJWBfhHDUd+AtIgLSCUZ0v +YhTLFlFY7hUEYRMqk3/tAxLw6dshXla9TSyzkJe3lijJabWGaMGqDgGC+/0puw=='
正如评论中已经提到的,必须调整 Java 方法
signHash()
才能产生与相应 .NET 方法相同的结果:
NonewithRSA
必须用作算法。经过必要更改的相关方法是:
private static String signHash(String hash, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("NonewithRSA"); // apply NoneWithRSA
System.out.println(signature.getProvider().getName());
signature.initSign(privateKey);
signature.update(HexFormat.of().parseHex("3031300d060960864801650304020105000420")); // completet Hash: s. RFC8017, 9.2, Notes, 1.
signature.update(Base64.getDecoder().decode(hash));
byte[] signedBytes = signature.sign();
return Base64.getEncoder().encodeToString(signedBytes);
}
请注意,签名不符合 RFC8017,因为哈希不是使用 SHA256 生成的,而是使用 HMAC/SHA256 生成的。