9

I am making a web application that distributes data encrypted with the public keys of its clients. At this moment this is working for dedicated devices which I distribute myself. Before they are shipped, I flash the public/private keypair into the devices firmware.

However since this is 2017, I also wanted to make an android application to make any android device act like one of the devices I sell, since this is easier for customers. The only bottleneck I am experiencing is the distribution of the keys.

I can generate a public/private keypair at the Android device upon first start of the app, and securely store them in the internal storage, but I need to find a way to send the generated public key of the Android device to my server, so my server can encrypt the data with it.

The reason why I cannot simply send the public key as a HTTP request to my server is, that I do not want someone with a computer to be able to generate a public/private keypair and send that to my server to also be able to receive data as. It's very easy to decompile Android applications or use wireshark to find out the url used to register new keys.

If I were to use this way, how would I verify at my servers end that the request is originating from a legitimate Android device?

CryptoGuy51
  • 91
  • 1
  • 1
  • 2
  • Do the users of that app have any way to register/authenticate themselves apart from the certificates? You can embed a general certificate for all android apps of yours and once the device is authenticated/registered you can ask them to send the public key encrypted by your public key to you – Limit Feb 02 '17 at 22:38
  • [Secure way to hold private keys in the Android app](https://security.stackexchange.com/a/242398/118310) – defalt Jan 03 '21 at 10:18
  • You should not use the public key (of the server) to encrypt the data on the wire (i.e.: client's public key). However, you can pin that key of the server to do the SSL handshake and then generate a symmetric session key between the you/server and clients so everything is confidential. This should be complemented by an enrolment system so the serve knows who's authorised to do/receive what. – zgulser May 15 '21 at 11:12

2 Answers2

6

Edit: as mentioned in the comments, this can be bypassed. To verify a user is legitimate with certainty, another process must be used.

As Limit states, and thanks to this answer, an okay way would be to:

  1. Generate a keypair for encrypting on your dev server

    openssl genrsa -des3 -out private.pem 2048

    openssl rsa -in private.pem -outform PEM -pubout -out public.pem

  2. Store the public key in the application code, doesn't matter where. This will be used in the function at step 5. Maybe obfuscate the code to prevent altering of the encrypting mechanism. (encrypting will only prevent tampering in transit with something like burp proxy. smali skills anyone?)

  3. Generate a keypair on the device ( you mentioned you had this down, but just in case anyone wonders )

    public static KeyPair getKeyPair() {
        KeyPair kp = null;
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(2048);
            kp = kpg.generateKeyPair();
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return kp; 
    }
    
  4. Isolate the public keys and store them where you want. Store the private key in the secure store.

    KeyPair clientKeys = getKeyPair();
    publicKey = clientKeys.getPublic();
    
  5. Encrypt the client public key with the embedded dev public key so you can send it home.

    public static String encryptToRSAString(String clearText, String publicKey) {
        String encryptedBase64 = "";
        try {
            KeyFactory keyFac = KeyFactory.getInstance("RSA");
            KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKey.trim().getBytes(), Base64.DEFAULT));
            Key key = keyFac.generatePublic(keySpec);
            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
            // encrypt the plain text using the public key
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(clearText.getBytes("UTF-8"));
            encryptedBase64 = new String(Base64.encode(encryptedBytes, Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
        }
            return encryptedBase64.replaceAll("(\\r|\\n)", "");
    }
    
J.A.K.
  • 4,793
  • 13
  • 30
  • 1
    Caveat: this will not prevent anyone from extracting the dev public key and still using it. This is just adding an extra step to increase obscurity, not security. – J.A.K. Jan 09 '18 at 13:52
  • Does that mean there is no 100% fool-proof way to validate the request is originating from a legitimate Android device? – Franklin Sep 20 '18 at 05:44
  • @Franklin the user has physical access to the device, whatever you give to the apk to sign data can theoretically be extracted. It can only be made harder to do so. – J.A.K. Sep 21 '18 at 11:07
0

So may be you want to receive keys only from registered users? Authentication will require in the end some shared secret between your server and registered user. With this secret, you can try to exchange keys using some good protocol of key exchange, for example, EKE or similar protocol. https://en.wikipedia.org/wiki/Encrypted_key_exchange

But, if you have SSL certificate of the server (and I assume you have), when you establish connection with registered user, he can send you his public pair in this channel, because it's already encrypted connection. Also considered better practice not to develop/implement security protocols of your own or even known ones, so this option is probably better.

By SSL sertificate of the server I mean sertificate that signed by trusted CA authority

  • Welcome to infosec SE!. Good point, but i think he's not worried that much about people sniffing the key (confidentiality) as much as users not modifying it (integrity) – J.A.K. Feb 03 '17 at 01:11