【问题标题】:Android/Python How to verify Signature SHA256withRSA and PKCS1 PaddingAndroid/Python 如何验证签名 SHA256withRSA 和 PKCS1 填充
【发布时间】:2017-12-11 08:56:23
【问题描述】:

我是新会员,我已经等了两天才找到解决方案验证从 android 客户端到 python 服务器的签名。 首先,我创建密钥对并从私钥生成签名。谢谢 pedrob 我已经更新了完整的代码。在 python 服务器中验证完成。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    KeyPairGenerator keyPairGenerator = null;
    try {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        //keyStore.deleteEntry("key1");
        keyPairGenerator = KeyPairGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
        try {
            KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("key1", KeyProperties.PURPOSE_SIGN)
                    .setKeySize(2048)
                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                    .setDigests(KeyProperties.DIGEST_SHA256)
                    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
            keyPairGenerator.initialize(builder.build());
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null);
        PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
        String publicKeyStr = Base64.encodeToString(publicKey.getEncoded(), Base64.NO_WRAP);
        Log.d("Hahaha", publicKeyStr);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        String data = "haha";
        signature.update(data.getBytes());
        byte[] signatureBytes = signature.sign();
        String signatureBase64 = Base64.encodeToString(signatureBytes, Base64.NO_WRAP);
        Log.d("Hahaha", signatureBase64);
        Signature verifySignature = Signature.getInstance("SHA256withRSA");
        verifySignature.initVerify(publicKey);
        verifySignature.update(data.getBytes());
        boolean isVerify = verifySignature.verify(Base64.decode(signatureBase64, Base64.NO_WRAP));
        Log.d("Hahaha", isVerify + "");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static String sha256(String rawString){
    MessageDigest shaDigest;
    byte[] data;
    try {
        data = rawString.getBytes("UTF-8");
        shaDigest = MessageDigest.getInstance("SHA-256");
    } catch (Exception e) {
        return null;
    }
    shaDigest.update(data);
    return toHex(shaDigest.digest());
}

public static String toHex(byte[] tmp) {
    char hexDigits[] =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
                    'e', 'f'};
    int nBytesLen = tmp.length;
    char str[] = new char[nBytesLen * 2];
    int k = 0;
    for (byte byte0 : tmp) {
        str[k++] = hexDigits[byte0 >>> 4 & 0xf];
        str[k++] = hexDigits[byte0 & 0xf];
    }
    return new String(str);
}

和服务器,我使用PKCS1_v1_5来验证签名,它将从android客户端发送

from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from base64 import b64decode

keyDER = b64decode(open('public.der').read())
key = RSA.importKey(keyDER)
message = "haha"
h = SHA256.new(message)
print h.hexdigest()
signature = b64decode(open('signature.der').read())
verifier = PKCS1_v1_5.new(key)
if verifier.verify(h, signature):
    print "The signature is authentic."
else:
    print "The signature is not authentic."

我检查了客户端和服务器的哈希数据,它是一样的,但是verifier.verify返回false,public.der是变量PublicKeyStr的值,signature.der是变量SignatureBase64的值 请帮帮我。

【问题讨论】:

  • 数据相同,Java端是“哈哈哈”,python端是“哈哈”。
  • 您有几个错误 1) 您在 Java 代码和签名和十六进制值中进行了两次散列。将signature.update(sha256(data).getBytes()); 更改为signature.update(data.getBytes()); 并将verifySignature.update(sha256(data).getBytes()); 更改为verifySignature.update(data.getBytes()); 2)删除.setBlockModes(KeyProperties.BLOCK_MODE_CBC) 3)您应该创建密钥对,而不是每次调用onCreate
  • 谢谢,但客户端我验证真实,我创建的 verifySignature 仅检查它是否适用于 android。我在java端和python端都签名的数据是“哈哈”。 python 中的 h.hexdigest() 和 sha256(data).getBytes() 返回相同的值。
  • 3) 您应该创建一个密钥对,而不是每次调用 onCreate 时。你是对的,所以我总是在调试模式调用 onCreate 之后将密钥对更新为 public.der 和 signature.der。
  • 谢谢佩德罗夫。我已经解决了这个问题,但我有一个问题要问你?我认为我们应该对客户端和服务器都进行哈希处理。但是为什么我们保留'haha'并在客户端签名但服务器必须通过SHA256哈希?

标签: android python python-2.7 rsa digital-signature


【解决方案1】:

[在 cmets 中解决] 你的 Java 代码中有几个错误

1) 在 Java 代码中,您使用 SHA256 对消息进行两次消化并签署一个十六进制值,而不是二进制消息。

在 java 中,SHA256withRSA 执行几个操作,首先对消息应用 SHA256 摘要算法,然后应用 RSA PKCS #1 v1.5 算法。在 python(或其他语言)中,它可以分两步以编程方式完成

改变

signature.update(sha256(data).getBytes());
verifySignature.update(sha256(data).getBytes());

signature.update(data.getBytes());
verifySignature.update(data.getBytes());

2) 删除.setBlockModes(KeyProperties.BLOCK_MODE_CBC)。这是一个加密参数,不适用于数字签名

3) 您应该创建一次密钥对,而不是每次调用onCreate

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-02
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多