1

I create to python3 application that generate the RSA key pairs.

from Crypto.PublicKey import RSA

print("--Private Key Generate--")

key = RSA.generate(2048)
private_key = key.export_key()
file_out = open("key/private.pem", "wb")
file_out.write(private_key)
file_out.close()

print("--Public Key Generate--")

public_key = key.publickey().export_key()
file_out_1 = open("key/receiver.pem", "wb")
file_out_1.write(public_key)
file_out_1.close()

print("key Generated")

I sign some data using python and create a signature. It is also verified using python successfully.

 def sign(data):
    private_key = RSA.import_key(open('key/private.pem').read())
    h = SHA256.new(data)
    signature =  base64.b64encode(pss.new(private_key).sign(h))
    print("signature generate")
    verify(data,signature)
    return signature


def verify(recive_Data ,signature):
    public_key = RSA.import_key(open('key/receiver.pem').read())
    h =  SHA256.new(recive_Data)
    verifier = pss.new(public_key)
    try:
        verifier.verify(h, base64.b64decode(signature))
        print("The signature is authentic")
    except (ValueError, TypeError):
        print ("The signature is not authentic.")

But actually, my verification implementation in Android(min SDK 23, target SDK 29). So, I need to convert this verification code to Android. I tried using the following code, but not verification success. need some expert help to do it.

public class SecurityHelper {

    private static String getKey(InputStream filename) throws IOException {
        // Read key from file
        String strKeyPEM = "";
        BufferedReader br = new BufferedReader(new InputStreamReader(filename));
        String line;
        while ((line = br.readLine()) != null) {
            strKeyPEM += line + "\n";
        }
        br.close();
       // System.out.println(strKeyPEM);
        return strKeyPEM;
    }


    public static PublicKey getPublicKey(InputStream filename) throws IOException, GeneralSecurityException {
        String publicKeyPEM = getKey(filename);
        return getPublicKeyFromString(publicKeyPEM);
    }

    public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
        String publicKeyPEM = key;
        publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
        publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
        System.out.println(publicKeyPEM);
        byte[] encoded = Base64.decode(publicKeyPEM ,Base64.CRLF);
      //  System.out.println(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PublicKey pubKey = kf.generatePublic(new X509EncodedKeySpec(encoded));
        System.out.println(pubKey);
        return pubKey;
    }


  public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException {

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initVerify(publicKey);
        sign.update(message.getBytes("UTF-8"));
        System.out.println(message);
        return  sign.verify(Base64.decode(signature,Base64.CRLF));
    }


}
kelalaka
  • 5,064
  • 5
  • 27
  • 44
uma
  • 1,477
  • 3
  • 32
  • 63

1 Answers1

2

There are different paddings used, in the Python code PSS and in the Android code Pkcs#1 v1.5, see for the difference RFC 8017. Replace in the Android code SHA256withRSA with SHA256withRSA/PSS.

Update:

Although according to the Android documentation SHA256withRSA/PSS is supported from API level 23+, an InvalidKeyException (No provider supports the provided key) is thrown for API level 23, for API level 24+ it works as specified.

A possible workaround for API level 23 is to use BouncyCastle, which then has to be included as a dependency in the Android project (details depend on the IDE, e.g. here for Android Studio):

implementation 'org.bouncycastle:bcprov-jdk15on:1.64'

Before adding the BC Provider, the pre-installed version must be removed. The schema to be used is SHA256withRSAandMGF1 (see section Signature Algorithms):

Security.removeProvider("BC"); 
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
Signature sign = Signature.getInstance("SHA256withRSAandMGF1"); 
// Go ahead as for schema SHA256withRSA/PSS...

Note: SpongyCastle would be an alternative possibility. Here the pre-installed BC Provider doesn't have to be removed. The schema is SHA256withRSA/PSS.

Community
  • 1
  • 1
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • when change it , given a error "W/System.err: java.security.InvalidKeyException: No provider supports the provided key" like that, error came this line ' sign.initVerify(publicKey);' as i understand , it is some thing worng , when create a public key? – uma Mar 16 '20 at 02:31
  • 1
    @uma - I can reproduce the exception (`java.security.InvalidKeyException: No provider supports the provided key`) for API level 23. For API levels 24 and above it seems to work (at least for the API levels I tested: 24, 26 and 28). According to the specification, [`SHA256withRSA/PSS`](https://developer.android.com/reference/java/security/Signature) should work for API level 23+ and above. Maybe a bug, but I'm not sure. – Topaco Mar 16 '20 at 09:45
  • 1
    For API level 23, a possible workaround would be to add BouncyCastle _explicitly_ as a dependency to the Android project and use the schema `SHA256withRSAandMGF1`. For this, make sure to remove in the code first the pre-installed BC provider (`Security.removeProvider("BC")`) before adding the new BC provider (`Security.addProvider(new BouncyCastleProvider()`). This works on my machine. – Topaco Mar 16 '20 at 12:45
  • can you please put answer. ??? It is big help to me, I coudn;t update min sdk vertion to 24. :'( – uma Mar 16 '20 at 13:03