Hyperledger Web3j: HSM support for AWS KMS
In the world of digital security, protecting sensitive data with robust encryption is essential. AWS Key Management Service (KMS) plays a crucial role in this space. It serves as a highly secure, fully managed service for creating and controlling cryptographic keys. What many may not realize is that AWS KMS itself operates as a Hardware Security Module (HSM), offering the same level of security you'd expect from dedicated hardware solutions.
An HSM is a physical device designed to securely generate, store, and manage encryption keys, and AWS KMS delivers this functionality in a cloud-native way. Beyond key management, AWS KMS with HSM support can also be used to sign cryptographic transactions. This provides a trusted, hardware-backed way to secure blockchain interactions, digital signatures, and more. This article will cover how AWS KMS functions as an HSM, the benefits of using it to sign crypto transactions, and how it fits into a broader security strategy.
In Hyperledger Web3j, support for HSM was introduced two years ago, providing users with a secure method for managing cryptographic keys. For more details, you can refer to the official documentation.
However, despite this integration, many users have encountered challenges in adopting and implementing HSM interfaces, particularly when using the AWS KMS module. To address these difficulties, a ready-to-use implementation has been added specifically for AWS KMS HSM support. This simplifies the integration process, making it easier for users to leverage AWS KMS for secure transaction signing without the complexity of manual configurations.
The class, HSMAwsKMSRequestProcessor, is an implementation of the HSMRequestProcessor interface, which is responsible for facilitating interaction with an HSM. This newly implemented class contains all the essential code required to communicate with AWS KMS, enabling the retrieval of data signed with the correct cryptographic signature. It simplifies the process of using AWS KMS as an HSM by handling the intricacies of signature generation and ensuring secure transaction signing without additional development overhead.
Here is a snippet with the most important actions of the callHSM method:
@Override
public Sign.SignatureData callHSM(byte[] dataToSign, HSMPass pass) {
// Create the SignRequest for AWS KMS
var signRequest =
SignRequest.builder()
.keyId(keyID)
.message(SdkBytes.fromByteArray(dataHash))
.messageType(MessageType.DIGEST)
.signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
.build();
// Sign the data using AWS KMS
var signResult = kmsClient.sign(signRequest);
var signatureBuffer = signResult.signature().asByteBuffer();
// Convert the signature to byte array
var signBytes = new byte[signatureBuffer.remaining()];
signatureBuffer.get(signBytes);
// Verify signature osn KMS
var verifyRequest =
VerifyRequest.builder()
.keyId(keyID)
.message(SdkBytes.fromByteArray(dataHash))
.messageType(MessageType.DIGEST)
.signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
.signature(SdkBytes.fromByteArray(signBytes))
.build();
var verifyRequestResult = kmsClient.verify(verifyRequest);
if (!verifyRequestResult.signatureValid()) {
throw new RuntimeException("KMS signature is not valid!");
}
var signature = CryptoUtils.fromDerFormat(signBytes);
return Sign.createSignatureData(signature, pass.getPublicKey(), dataHash);
}
NOTE!
In order to use this properly, the type of key spec created in AWS KMS must be ECC_SECG_P256K1. This is specific to the crypto space, especially to EVM. Using any other key will result in a mismatch error when the data signature is created.
Example
Here is a short example of how to call the callHSM method from the library:
public static void main(String[] args) throws Exception {
KmsClient client = KmsClient.create();
// extract the KMS key
byte[] derPublicKey = client
.getPublicKey((var builder) -> {
builder.keyId(kmsKeyId);
})
.publicKey()
.asByteArray();
byte[] rawPublicKey = SubjectPublicKeyInfo
.getInstance(derPublicKey)
.getPublicKeyData()
.getBytes();
BigInteger publicKey = new BigInteger(1, Arrays.copyOfRange(rawPublicKey, 1, rawPublicKey.length));
HSMPass pass = new HSMPass(null, publicKey);
HSMRequestProcessor signer = new HSMAwsKMSRequestProcessor(client, kmsKeyId);
signer.callHSM(data, pass);
}
Conclusion
AWS KMS, with its built-in HSM functionality, offers a powerful solution for securely managing and signing cryptographic transactions. Despite initial challenges faced by users in integrating AWS KMS with Hyperledger Web3j, the introduction of the HSMAwsKMSRequestProcessor class has made it easier to adopt and implement. This ready-to-use solution simplifies interactions with AWS KMS, allowing users to securely sign data and transactions with minimal configuration. By leveraging this tool, organizations can enhance their security posture while benefiting from the convenience of AWS’s cloud-native HSM capabilities.