使用AWS KMS服务签署以太坊,无法从返回的签名中获取正确的R和S值

问题描述 投票:0回答:1
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值,导致失败。有谁知道怎么解决吗

amazon-web-services digital-signature web3-java
1个回答
0
投票

确保在 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));
© www.soinside.com 2019 - 2024. All rights reserved.