0

I am trying to create digital signature of the hash (created using HMACSHA256) in Java using SHA256 algorithm & Pkcs1 RSA signature padding but it is not producing the same signature as implemented in .net by vendor.

My Java code :

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 is adding signature to a already hashed message whereas SignMessage is adding hash again to already hashed message and then adding signature. SignMessage is making the correct signature as per the vendor but SignHash is creating different.

Dotnet code implementation for SignHash function by vendor :

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() is generating same signature as signMessage() but signHash() and SignHashWithFile() are generating different signatures.

Sample values for .NET:

Data before hash: '512|PUB55442201|3555101122526|20000'

Data after hash: 'vGEWCW/XP7NPFIrY4sOo+jE4Q713RfVlKV/VV8ZQG6I='

signature(signhash): 'm3CHGVvBy90CVVnPzzFYQAekN6oy/isfz+fAVyXsJ/COTZ/fCMemANy0Q+yWlh/WUkSGbS7SQrc/33m+Wy69XrctOYSZcWMZpGFGBwBrCkg+wmscBfYItBqKlqmbQr2W0CIxOJlj5fLTeWYQ0MLCrMoBB9g1sB93eECYy9G5MzNfrRQaHVVzQrvBdyphABf8GsIuOzfjTOca5jGCZq6+0i/Xnb49jpkr3bPG3JP2+HbwKbyeqzB59pQaQ1+ye1pE7w85b0Sd3WDSfyjmYDBcNUooaMzekROaR8GPvlerhr1LbXwzvhoDON3ZJhFjerSikcrrM1JeL+s0DOCSQdvsBg=='

signature(signData): 'wLDprhCgUVG3V0pgU6DCZDQyoHf3VSQ7PMs9T6Ns3OM/lIz2cvACSVFJIs5W5sR7NN/jcp8k6yjQreq9SuSi9EPJsHFNlsqDjfqt78KOkqVD41PlFKRguPi9caMskRSKF/Ja1npXEweQw0MLgxGhs1sOffEzgk7Tqq4N3p2LN4mxgBtxoK3S+HfybPbHKpeSqx6Y0dVfz+Axa3v0/ChNU1+ZHA68H7+j7opdtaXTsx4dzodMvzdpfmFOpC9ye+mhmbJWBfhHDUd+AtIgLSCUZ0v+YhTLFlFY7hUEYRMqk3/tAxLw6dshXla9TSyzkJe3lijJabWGaMGqDgGC+/0puw=='

Sample values for Java :

Data before hash: '512|PUB55442201|3555101122526|20000'

Data after hash: 'vGEWCW/XP7NPFIrY4sOo+jE4Q713RfVlKV/VV8ZQG6I='

signature(signhash): 'W1U84sux5el9Tij8Dd9Tu2VbcXC2/BUwZFn5qeYh9M1D/ym6WjahyseTqUb6QFLuy1I9AAQN7p71NvYm1XpKKSGlJMl97cyHPijH3vj/+ydceLWm6Z4cIcJHrZsRld9EYNfYIW7e3h7UVQRwexZaNs+PjcWDAtuUWZevCGnQNzu20D3dIhdJdcZUp/J0rRO0rrWl9njXWDshiZz9mnVYx2wnoOJC6+2j2UBQOezupBzA5ZcvWaTrJhnes0l8aKhgwcdfb59ifuwhvcrFKBTxLkFVI7eMJslluHcFe2BYVjhmOQkqVfbkTYXgBqEsiQjCNdHlYsY15ZSbVDNpt9fthA=='

signature(signData): 'wLDprhCgUVG3V0pgU6DCZDQyoHf3VSQ7PMs9T6Ns3OM/lIz2cvACSVFJIs5W5sR7NN/jcp8k6yjQreq9SuSi9EPJsHFNlsqDjfqt78KOkqVD41PlFKRguPi9caMskRSKF/Ja1npXEweQw0MLgxGhs1sOffEzgk7Tqq4N3p2LN4mxgBtxoK3S+HfybPbHKpeSqx6Y0dVfz+Axa3v0/ChNU1+ZHA68H7+j7opdtaXTsx4dzodMvzdpfmFOpC9ye+mhmbJWBfhHDUd+AtIgLSCUZ0v+YhTLFlFY7hUEYRMqk3/tAxLw6dshXla9TSyzkJe3lijJabWGaMGqDgGC+/0puw=='

7
  • 1
    If on the .NET side message is a Base64 encoded SHA256 hash (i.e. hash is a SHA256 hash), on the Java side hash must be completed to the DER encoding of the DigestInfo value (see RFC8017) and as digest NoneWithRSA must be applied. Commented Nov 3, 2024 at 15:08
  • Hash generating at both .NET and Java are same, issue is coming while adding signature. @Topaco Commented Nov 4, 2024 at 5:47
  • Your explanations are somewhat confusing. I would recommend that you illustrate the problem using sample data: message, hash, key, signature. Commented Nov 4, 2024 at 6:41
  • I have added the sample data as well, I want to create the same signature for signhash in java as it is producing in .NET. You can use any public private key pair 2048 but required to be produced same signature for signhash. @Topaco Commented Nov 4, 2024 at 7:18
  • Without the key, the data is worthless for a repro. Post the corresponding key. If secret, use a test key and create the data again. Commented Nov 4, 2024 at 7:20

1 Answer 1

1

As already mentioned in the comment, the Java method signHash() must be adapted to produce the same result as the corresponding .NET method:

  • The hash must be completed to the DER encoding of the DigestInfo value.
  • NonewithRSA must be used as algorithm.

The method in question with the necessary changes is:

private static String signHash(String hash, PrivateKey privateKey) throws Exception {
    Signature signature = Signature.getInstance("NonewithRSA"); // apply NoneWithRSA
    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);
}

Note that the signature is not RFC8017 compliant, as the hash was not generated with SHA256, but with HMAC/SHA256.

Sign up to request clarification or add additional context in comments.

1 Comment

It works perfectly fine. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.