import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
import com.amazonaws.services.kms.model.GetPublicKeyRequest;
import com.amazonaws.services.kms.model.SignRequest;
import com.amazonaws.util.BinaryUtils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.\*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
@Slf4j
public class AwsKmsTest {
private static final String keyId = "****";
AWSKMS kmsClient = AWSKMSClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("****", "*****")))
.withRegion(Regions.AP_NORTHEAST_1)
.build();
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
@Test
public void publicKey() throws Exception {
BCECPublicKey publicKey = (BCECPublicKey) getPublicKey();
System.out.println(Keys.getAddress(pubKeyToBigInter(publicKey)));
}
@Test
public void sign() throws Exception {
String msg = "hi";
byte[] Bytes = msg.getBytes(StandardCharsets.UTF_8);
byte[] digestBytes = MessageDigest.getInstance("SHA-256").digest(Bytes);
SignRequest signRequest = new SignRequest();
signRequest.setKeyId(keyId);
signRequest.setMessage(ByteBuffer.wrap(digestBytes));
signRequest.setMessageType("DIGEST");
signRequest.setSigningAlgorithm("ECDSA_SHA_256");
ByteBuffer signatureB = kmsClient.sign(signRequest).getSignature();
BCECPublicKey publicKey = (BCECPublicKey) getPublicKey();
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initVerify(publicKey);
signature.update(Bytes);
if (signature.verify(signatureB.array())) {
ECDSASignature ecdsaSignature = parseDERSequence(signatureB.array());
//throw exception:Could not construct a recoverable key. Are your credentials valid?
Sign.SignatureData signMessage = Sign.createSignatureData(ecdsaSignature,
pubKeyToBigInter(publicKey),
Hash.sha3(Bytes));
System.out.println("0x" + Hex.toHexString(signMessage.getR()) + Hex.toHexString(signMessage.getS()) + Hex.toHexString(signMessage.getV()));
}
}
public static BigInteger pubKeyToBigInter(BCECPublicKey publicKey) {
byte[] publicKeyBytes = publicKey.getQ().getEncoded(false);
return new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length));
}
public PublicKey getPublicKey() throws Exception {
GetPublicKeyRequest publicKeyRequest = new GetPublicKeyRequest();
publicKeyRequest.setKeyId(keyId);
ByteBuffer publicKeyByteBuffer = kmsClient.getPublicKey(publicKeyRequest).getPublicKey();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(BinaryUtils.copyBytesFrom(publicKeyByteBuffer));
KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
return keyFactory.generatePublic(keySpec);
}
private ECDSASignature parseDERSequence(byte[] derEncoded) throws IOException {
ASN1InputStream asn1InputStream = new ASN1InputStream(derEncoded);
ASN1Sequence asn1Sequence = (ASN1Sequence) asn1InputStream.readObject();
ASN1Integer r = (ASN1Integer) asn1Sequence.getObjectAt(0);
ASN1Integer s = (ASN1Integer) asn1Sequence.getObjectAt(1);
BigInteger rValue = r.getPositiveValue();
BigInteger sValue = s.getPositiveValue();
return new ECDSASignature(rValue, sValue);
}
}
Sign.createSignatureData(ecdsaSignature,pubKeyToBigInter(publicKey),Hash.sha3(Bytes)); 抛出异常:无法构造可恢复的密钥。您的凭证有效吗?
大致可以确认parseDERSequence方法返回了不正确的f和s值,导致失败。有谁知道怎么解决吗
确保在 KMS.SignRequest 中指定 MessageType: 'DIGEST',否则,AWS 将尝试再次对您的负载进行哈希处理,从而生成无效签名。有效负载需要是交易对象的 keccak256 哈希值。
byte[] 字节 = msg.getBytes(StandardCharsets.UTF_8); byte[] 摘要Bytes = MessageDigest.getInstance("SHA-256").digest(Bytes);
替换为
byte[] Bytes = Hash.sha3(msg.getBytes(StandardCharsets.UTF_8));