3

I'm new member, I have waited two days to find solution verify signature from android client to python server. First I create Key pair and generate signature from private key. Thank you pedrofb I have updated full code. verify done in python server.

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

and Server, I use PKCS1_v1_5 to verify signature, it will be sent from android client

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."

I have check data hashed both client and server, it's same, but verifier.verify return false, public.der is value of variable PublicKeyStr and signature.der is value of variable SignatureBase64 Please help me.

pedrofb
  • 37,271
  • 5
  • 94
  • 142
Công Hải
  • 4,961
  • 3
  • 14
  • 20
  • The data is *not* the same, it is "Hahaha" on the Java side and "haha" on the python side. – President James K. Polk Dec 11 '17 at 12:47
  • You have several errors 1) You are hashing twice in Java code and signing and hex value. Change `signature.update(sha256(data).getBytes());` with `signature.update(data.getBytes());` and `verifySignature.update(sha256(data).getBytes());` with `verifySignature.update(data.getBytes());` 2) Remove `.setBlockModes(KeyProperties.BLOCK_MODE_CBC)` 3) You should create the the keypair one, not each time `onCreate` is called – pedrofb Dec 11 '17 at 13:01
  • Thank you, but client I verify true, verifySignature I create only check it works from android. The data I sign both java side and python side is "haha". h.hexdigest() in python and sha256(data).getBytes() return same value. – Công Hải Dec 12 '17 at 01:52
  • 3) You should create the the keypair one, not each time onCreate is called. You are right, so I always update keypair to public.der and signature.der after onCreate call by debug mode. – Công Hải Dec 12 '17 at 01:52
  • Thank you pedrofb. I have resolved this issue, but I have a question for you? I think we should hash 'haha' both client and server. but why we keep 'haha' and sign it in client but server must be hash by SHA256? – Công Hải Dec 12 '17 at 02:03
  • @hai, I added the explanation as an answer – pedrofb Dec 12 '17 at 07:07

1 Answers1

1

[Solved in comments] You have several errors in your Java code

1) In the Java code, you are digesting the message twice with SHA256 and signing an hex value, not the binary message.

In java, SHA256withRSA performs several operations, first applies the SHA256 digest algorithm to the message and then applies the RSA PKCS # 1 v1.5 algorithm. In python (or in other languages) it can be done programmatically in two steps

Change

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

with

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

2) Remove .setBlockModes(KeyProperties.BLOCK_MODE_CBC). This is an encryption parameter not appliable to digital signature

3) You should create the the keypair once, not each time onCreate is called

pedrofb
  • 37,271
  • 5
  • 94
  • 142