如何使用公钥验证 ECDSA?

Posted

技术标签:

【中文标题】如何使用公钥验证 ECDSA?【英文标题】:How to verify ECDSA with Public Key? 【发布时间】:2019-05-20 09:07:30 【问题描述】:

我有以下方法来生成密钥和签署密码消息。

public void generateKeys() 
try 
  keyStore = KeyStore.getInstance(KEYSTORE_NAME);
  keyStore.load(null);

  if (!keyStore.containsAlias(KEY_NAME)) 
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, KEYSTORE_NAME);
    keyPairGenerator.initialize(
        new KeyGenParameterSpec.Builder(KEY_NAME,
                                        KeyProperties.PURPOSE_SIGN)
            .setDigests(KeyProperties.DIGEST_SHA256)
            .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
            // Require the user to authenticate with a fingerprint to authorize
            // every use of the private key
            .setUserAuthenticationRequired(true)
            .build());
    keyPairGenerator.generateKeyPair();
  

  loadKeys();
 catch (NoSuchAlgorithmException e) 
  e.printStackTrace();
 catch (NoSuchProviderException e) 
  e.printStackTrace();
 catch (InvalidAlgorithmParameterException e) 
  e.printStackTrace();
 catch (CertificateException e) 
  e.printStackTrace();
 catch (KeyStoreException e) 
  e.printStackTrace();
 catch (IOException e) 
  e.printStackTrace();

public BiometricPrompt.CryptoObject getCryptoObject() 
cryptoObject = new BiometricPrompt.CryptoObject(signature);
return cryptoObject;


private void loadKeys() 
try 
  keyStore = KeyStore.getInstance(KEYSTORE_NAME);
  keyStore.load(null);
  if (keyStore.containsAlias(KEY_NAME)) 
    publicKey = keyStore.getCertificate(KEY_NAME).getPublicKey();
    privateKey = (PrivateKey) keyStore.getKey(KEY_NAME, null);
    signature = Signature.getInstance(Constants.SIGNATURE);
    signature.initSign(privateKey);
  
 catch (IOException e) 
  e.printStackTrace();
 catch (NoSuchAlgorithmException e) 
  e.printStackTrace();
 catch (CertificateException e) 
  e.printStackTrace();
 catch (KeyStoreException e) 
  e.printStackTrace();
 catch (UnrecoverableKeyException e) 
  e.printStackTrace();
 catch (InvalidKeyException e) 
  e.printStackTrace();



public String sign(String inputStr) 
try 
  Signature signature = cryptoObject.getSignature();
  signature.update(inputStr.getBytes());
  byte[] signedBytes = signature.sign();
  String result = HexManager.bytesToHex(signedBytes);
  Log.d("TAG", result);
  return result;
 catch (SignatureException e) 
  e.printStackTrace();

return null;

之后,我将签名密码保存在共享首选项中。稍后,我想用指纹验证的新密码验证保存的密码。

这是我的验证方法:

public boolean verify(String inputStr, String savedStr) 
try 
  Signature signature = cryptoObject.getSignature();
  signature.initVerify(publicKey);
  signature.update(inputStr.getBytes());
  boolean isVerified = signature.verify(savedStr.getBytes());
  return isVerified;
 catch (InvalidKeyException e) 
  e.printStackTrace();
 catch (SignatureException e) 
  e.printStackTrace();

return false;

但它总是返回 false。

有人知道为什么吗?

【问题讨论】:

您不应该在签名验证过程中的某个时刻撤消bytesToHex 步骤吗? 【参考方案1】:

看起来在您的sign() 方法中,您正在返回从signature.sign() 方法获得的byte[]Hex。如果这是您保存为savedStr 的内容。那么验证方式要改成将Hex编码的字符串转换为byte[]。您可以使用HexManager.hexToBytes()(或等效项)(如果存在)将savedStr 转换为byte[] savedStrBytes

public boolean verify(String inputStr, String savedStr) 
    try 
        byte[] savedStrBytes = HexManager.hexToByes(savedStr);
        Signature signature = cryptoObject.getSignature();
        signature.initVerify(publicKey);
        signature.update(inputStr.getBytes());
        boolean isVerified = signature.verify(savedStrBytes);
        return isVerified;
     catch (InvalidKeyException e) 
        e.printStackTrace();
     catch (SignatureException e) 
        e.printStackTrace();
    
    return false;

【讨论】:

以上是关于如何使用公钥验证 ECDSA?的主要内容,如果未能解决你的问题,请参考以下文章

ECDSA Android 使用公钥验证总是返回 false

如何使用证书中的公钥检查 ECDSA(在 p-256 上)签名

当我只有原始签名(R,S)和原始公钥点(Qx,Qy)时,ECDSA如何验证Java中的数据块

如何在 C# 中从 ECDsa 获取公钥

如何使用 Hyperledger Fabric 中生成的 ECDSA 私钥和公钥进行加密和解密

如何从 R 中的散列消息和签名中正确恢复 ECDSA 公钥 ||小号 || V格式?