Verifying hmac sha1 signature in Java

agerrr picture agerrr · Sep 8, 2013 · Viewed 12.8k times · Source

I'd like to know how I could verify the signature I created. My code to create a signature looks similar to this one: HMAC-SHA1: How to do it properly in Java?

I send the message, the signature and the public key to verify the signature. Public and private key are generated using KeyPairGenerator. How can I use the public key I have to verify my signature? Or maybe can you suggest any good libraries for Java for signing and verifying signature that use HMAC SHA1?

Answer

gtrig picture gtrig · Sep 9, 2013

First to clarify, the HMAC code does not generate a signature. It is a type of Message Authentication Code (MAC).

The latter link explains the difference between a signature and a MAC this way:

MACs differ from digital signatures as MAC values are both generated and verified using the same secret key. This implies that the sender and receiver of a message must agree on the same key before initiating communications, as is the case with symmetric encryption. For the same reason, MACs do not provide the property of non-repudiation offered by signatures specifically in the case of a network-wide shared secret key: any user who can verify a MAC is also capable of generating MACs for other messages. In contrast, a digital signature is generated using the private key of a key pair, which is asymmetric encryption. Since this private key is only accessible to its holder, a digital signature proves that a document was signed by none other than that holder. Thus, digital signatures do offer non-repudiation. However, non-repudiation can be provided by systems that securely bind key usage information to the MAC key; the same key is in possession of two people, but one has a copy of the key that can be used for MAC generation while the other has a copy of the key in a hardware security module that only permits MAC verification. This is commonly done in the finance industry.

So in order to verify an HMAC, you need to share the key that was used to generate it. You would send the message, the HMAC, and the receiver would have the same key you used to generate the HMAC. They could then use the same algorithm to generate an HMAC from your message, and it should match the HMAC you sent. Public/private keys (assymetric) are not used for this. You need to generate a symmetric key (like AES) and securely share that with the people that will be generating/verifying the HMAC.

This limits the HMAC to having integrity and authenticity properties only, and not non-repudiation.

The quote above mentioned that hardware security modules could be used to enforce the key use, and then you could get non-repudiation as long as only one person could use the key for generating the HMAC.

Alternatively, you could use a hybrid approach. Use a shared symmetric key to generate the HMAC. The HMAC in the end is a hash. You could then sign this hash with your private key (different than the key used in the HMAC). A third party with the symmetric key and your public key could verify you signed the HMAC, and could generate their own HMAC with the message and the shared key to make sure it matched. This would also give you non-repudiation.

If you want to go this route, use the Java Signature class. The HMAC algorithm is SHA-1, and assuming your keypair is RSA, you could use the NONEwithRSA Signature algorithm since the input is already a SHA-1 hash. Or you could hash it again with the SHA1withRSA algorithm. As long as you generate the signature and verify with the same algorithm, it should be OK.

    byte[] data = hmac.getBytes("UTF-8");

    Signature mySig = Signature.getInstance("NONEwithRSA");

    mySig.initSign(myPrivateKey);

    mySig.update(data);

    byte[] sigBytes = mySig.sign();

    // And to verify.

    Signature mySig2 = Signature.getInstance("NONEwithRSA");

    mySig2.initVerify(myPublicKey);

    boolean isSigValid = mySig2.verify(sigBytes);