Skip to content

Commit

Permalink
Merge pull request #2105 from hyperledger/feature/add_kms_support
Browse files Browse the repository at this point in the history
Add HSM kms implementation
  • Loading branch information
gtebrean authored Oct 4, 2024
2 parents 8fa31dd + cca45d1 commit 86df48c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
### Features

* bump snapshot version to 4.12.3 [#2101](https://github.com/hyperledger/web3j/pull/2101)
* Add HSM kms implementation [#2105](https://github.com/hyperledger/web3j/pull/2105)

### BREAKING CHANGES

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ext {
picocliVersion = '4.7.6'
ensAdraffyVersion = '0.2.0'
kzg4844Version = '2.0.0'
awsSdkVersion = '2.27.24'
tuweniVersion = '2.4.2'
// test dependencies
equalsverifierVersion = '3.16.1'
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
"io.github.adraffy:ens-normalize:$ensAdraffyVersion",
"io.tmio:tuweni-bytes:$tuweniVersion",
"io.tmio:tuweni-units:$tuweniVersion"
implementation "software.amazon.awssdk:kms:$awsSdkVersion"
testImplementation project(path: ':crypto', configuration: 'testArtifacts'),
"nl.jqno.equalsverifier:equalsverifier:$equalsverifierVersion",
"ch.qos.logback:logback-classic:$logbackVersion"
Expand Down
104 changes: 104 additions & 0 deletions core/src/main/java/org/web3j/service/HSMAwsKMSRequestProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2024 Web3 Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.web3j.service;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.MessageType;
import software.amazon.awssdk.services.kms.model.SignRequest;
import software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;
import software.amazon.awssdk.services.kms.model.VerifyRequest;

import org.web3j.crypto.CryptoUtils;
import org.web3j.crypto.HSMPass;
import org.web3j.crypto.Sign;

/**
* HSM request processor for AWS KMS. Notice the KMS key must be ECC_SECG_P256K1, this key is
* supported in crypto space.
*/
public class HSMAwsKMSRequestProcessor implements HSMRequestProcessor {

private KmsClient kmsClient;
private String keyID;

public HSMAwsKMSRequestProcessor(KmsClient kmsClient, String keyID) {
this.kmsClient = kmsClient;
this.keyID = keyID;
}

/**
* Entry point method which creates the KMS sign request
*
* @param dataToSign - data to be signed
* @param pass - public key of the asymmetric KMS key pair used for signing The @{@link
* org.web3j.crypto.HSMPass} should be instantiated before this method call. Use the
* following code for getting and setting the public key:
* <p>byte[] rawPublicKey = KmsClient.create() .getPublicKey((var builder) -> {
* builder.keyId(kmsKeyId); }) .publicKey() .asByteArray();
* <p>byte[] publicKey = SubjectPublicKeyInfo .getInstance(rawPublicKey) .getPublicKeyData()
* .getBytes();
* <p>BigInteger publicKey = new BigInteger(1, Arrays.copyOfRange(publicKey, 1,
* publicKey.length));
* <p>HSMPass pass = new HSMPass(null, publicKey);
* @return SignatureData v | r | s
*/
@Override
public Sign.SignatureData callHSM(byte[] dataToSign, HSMPass pass) {
byte[] dataHash = new byte[0];
try {
dataHash = MessageDigest.getInstance("SHA-256").digest(dataToSign);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(
"Algorithm SHA-256 is not available for the given data!");
}

// 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 on 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);
}
}

0 comments on commit 86df48c

Please sign in to comment.