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

Posted

技术标签:

【中文标题】ECDSA Android 使用公钥验证总是返回 false【英文标题】:ECDSA Android verify with Public Key always return false 【发布时间】:2017-08-26 14:57:47 【问题描述】:

我有一些令牌,我需要先使用 SHA256 和 ECDSA 根据 KeyStore 中的私钥和公钥对其进行签名。

每次我尝试验证值时,我都会得到错误的结果。我不知道为什么。

有人知道如何解决这个问题吗?

这是我生成和加载密钥的函数:

private 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 | KeyProperties.PURPOSE_VERIFY)
                            .setDigests(KeyProperties.DIGEST_SHA256,
                                    KeyProperties.DIGEST_SHA512)
                            .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
                            .setUserAuthenticationRequired(false)
                            .build());
            keyPairGenerator.generateKeyPair();

            setRegistred(true);
        
     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();
    




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);
        
     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();
    

这是符号值的方式:

 public String sign(String inputStr, FingerprintManager.CryptoObject cryptoObject)
    try 
        Signature signature = Signature.getInstance(SecurityConstants.SIGNATURE);
        signature.initSign(privateKey);
        signature.update(inputStr.getBytes());
        byte[] signedBytes = signature.sign();
        String result = Base64.encodeToString(signedBytes, Base64.DEFAULT);
        Log.d("TAG", result);
        return result;
     catch (SignatureException e) 
        e.printStackTrace();
     catch (InvalidKeyException e) 
        e.printStackTrace();
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
    
    return null;

这是我用公钥验证的尝试:

public boolean verifyWithPublicKey(String input, FingerprintManager.CryptoObject cryptoObject)
    try 
        Signature signature = Signature.getInstance(SecurityConstants.SIGNATURE);
        keyStore = KeyStore.getInstance(KEYSTORE_NAME);
        keyStore.load(null);
        PublicKey pk  = getPublicKeyForVerification();
        signature.initVerify(pk);
        signature.update(input.getBytes());
        boolean isVerifed = signature.verify(input.getBytes());
        Log.d("TAG", String.valueOf(isVerifed));
        return isVerifed;
     catch (SignatureException e) 
        e.printStackTrace();
     catch (InvalidKeyException e) 
        e.printStackTrace();
     catch (CertificateException e) 
        e.printStackTrace();
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
     catch (KeyStoreException e) 
        e.printStackTrace();
     catch (IOException e) 
        e.printStackTrace();
    
    return false;

【问题讨论】:

【参考方案1】:

错误就在这里,当你编写以下代码来验证签名时:

signature.update(input.getBytes());
boolean isVerifed = signature.verify(input.getBytes());

使用此代码,您尝试验证签名是否已与自己签名!

你应该有:

signature.update(MY_BYTES_ARRAY_OF_DATA);
boolean isVerifed = signature.verify(MY_SIGNATURE);

不要忘记签名默认不封装签名数据。

如果您想要包含签名数据和相关签名的格式,请使用 S/MIME、OpenPGP 等。

【讨论】:

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

如何使用使用 sha1ecdsa 的公钥验证数据是不是符合签名?

验证充气城堡上的 javacard 签名 ALG_ECDSA_SHA

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

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

C# ecdsa 签名 - 我可以选择哪个密钥规范?

sCrypt 中的 ECDSA 签名验证