RSA Interface Encryption and Decryption in Android

Keywords: Android Java github git

1.RSA introduction

RSA is an asymmetric encryption algorithm Encryption and decryption use different keys. Each communication party holds one of a pair of keys (called public key and private key). Only the other party's key can decrypt the data encrypted by its own key. RSA is based on the fact of number theory that it is very easy to multiply two large prime numbers, but it is extremely difficult to factorize the product. Therefore, the product can be publicly used as encryption key, i.e. public key, while the two large prime numbers can be combined into private key. The public key is available to anyone, and the private key is owned by itself for decryption.

Generally, RSA encryption in Android is used to interface data encryption and data decryption. We can encrypt the data sent to the background when requesting the interface, and then decrypt it according to the secret key you encrypt. The interface that backstage returns important data is the same as backstage data encryption. We decrypt the data according to the secret key provided by backstage, so as to achieve a certain degree of data security, even if the data obtained by grabbing the package is invisible.

Here's a demonstration of a set of local data encryption

Pick up the rendering first

We can see that the above encryption state uses symmetric encryption method, public key encryption and private key decryption, private key encryption and public key decryption to encrypt data.

2. An example of encryption in Android

1. This tool class is an encryption and decryption tool class, which includes two forms: public key encryption and private key decryption, private key encryption and public key decryption. It can be used directly.

public class RSAUtils {
    public static final String RSA = "RSA";// Asymmetric Encryption Key Algorithms
    public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//Encryption Filling Mode
    public static final int DEFAULT_KEY_SIZE = 2048;//Default length of secret key
    public static final byte[] DEFAULT_SPLIT = "#PART#". getBytes();// / When the content to be encrypted exceeds bufferSize, partition is used for block encryption
    public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// Maximum number of bytes that the current secret key supports encryption

    /**
     * Random Generation of RSA Key Pairs
     *
     * @param keyLength Key length, range: 512-2048
     *                  General 1024
     * @return
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Encryption of strings with public keys
     *
     * @param data original text
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // Get public key
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PublicKey keyPublic = kf.generatePublic(keySpec);
        // Encrypted data
        Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
        cp.init(Cipher.ENCRYPT_MODE, keyPublic);
        return cp.doFinal(data);
    }

    /**
     * secret key encryption
     *
     * @param data       Data to be encrypted
     * @param privateKey secret key
     * @return byte[] Encrypted data
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {
        // Get the private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);
        // data encryption
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
        return cipher.doFinal(data);
    }

    /**
     * Public key decryption
     *
     * @param data      Data to be decrypted
     * @param publicKey secret key
     * @return byte[] Declassified data
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // Get public key
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PublicKey keyPublic = kf.generatePublic(keySpec);
        // data decryption
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, keyPublic);
        return cipher.doFinal(data);
    }

    /**
     * Decryption using private key
     */
    public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
        // Get the private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);

        // Declassified data
        Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
        cp.init(Cipher.DECRYPT_MODE, keyPrivate);
        byte[] arr = cp.doFinal(encrypted);
        return arr;
    }

    /**
     * Sectional Encryption of Strings with Public Key
     *
     */
    public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception {
        int dataLen = data.length;
        if (dataLen <= DEFAULT_BUFFERSIZE) {
            return encryptByPublicKey(data, publicKey);
        }
        List<Byte> allBytes = new ArrayList<Byte>(2048);
        int bufIndex = 0;
        int subDataLoop = 0;
        byte[] buf = new byte[DEFAULT_BUFFERSIZE];
        for (int i = 0; i < dataLen; i++) {
            buf[bufIndex] = data[i];
            if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
                subDataLoop++;
                if (subDataLoop != 1) {
                    for (byte b : DEFAULT_SPLIT) {
                        allBytes.add(b);
                    }
                }
                byte[] encryptBytes = encryptByPublicKey(buf, publicKey);
                for (byte b : encryptBytes) {
                    allBytes.add(b);
                }
                bufIndex = 0;
                if (i == dataLen - 1) {
                    buf = null;
                } else {
                    buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
                }
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    /**
     * Segment encryption
     *
     * @param data       The raw data to be encrypted
     * @param privateKey Secret key
     */
    public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception {
        int dataLen = data.length;
        if (dataLen <= DEFAULT_BUFFERSIZE) {
            return encryptByPrivateKey(data, privateKey);
        }
        List<Byte> allBytes = new ArrayList<Byte>(2048);
        int bufIndex = 0;
        int subDataLoop = 0;
        byte[] buf = new byte[DEFAULT_BUFFERSIZE];
        for (int i = 0; i < dataLen; i++) {
            buf[bufIndex] = data[i];
            if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
                subDataLoop++;
                if (subDataLoop != 1) {
                    for (byte b : DEFAULT_SPLIT) {
                        allBytes.add(b);
                    }
                }
                byte[] encryptBytes = encryptByPrivateKey(buf, privateKey);
                for (byte b : encryptBytes) {
                    allBytes.add(b);
                }
                bufIndex = 0;
                if (i == dataLen - 1) {
                    buf = null;
                } else {
                    buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
                }
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    /**
     * Sectional decryption of public key
     *
     * @param encrypted Data to be decrypted
     * @param publicKey secret key
     */
    public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception {
        int splitLen = DEFAULT_SPLIT.length;
        if (splitLen <= 0) {
            return decryptByPublicKey(encrypted, publicKey);
        }
        int dataLen = encrypted.length;
        List<Byte> allBytes = new ArrayList<Byte>(1024);
        int latestStartIndex = 0;
        for (int i = 0; i < dataLen; i++) {
            byte bt = encrypted[i];
            boolean isMatchSplit = false;
            if (i == dataLen - 1) {
                // It's the end of data.
                byte[] part = new byte[dataLen - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPublicKey(part, publicKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            } else if (bt == DEFAULT_SPLIT[0]) {
                // This starts with split[0].
                if (splitLen > 1) {
                    if (i + splitLen < dataLen) {
                        // Not beyond the scope of data
                        for (int j = 1; j < splitLen; j++) {
                            if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                                break;
                            }
                            if (j == splitLen - 1) {
                                // Verify that the last split, without break, indicates that the split segment has been confirmed.
                                isMatchSplit = true;
                            }
                        }
                    }
                } else {
                    // split has only one, and it already matches
                    isMatchSplit = true;
                }
            }
            if (isMatchSplit) {
                byte[] part = new byte[i - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPublicKey(part, publicKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    /**
     * Sectional decryption using private key
     *
     */
    public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception {
        int splitLen = DEFAULT_SPLIT.length;
        if (splitLen <= 0) {
            return decryptByPrivateKey(encrypted, privateKey);
        }
        int dataLen = encrypted.length;
        List<Byte> allBytes = new ArrayList<Byte>(1024);
        int latestStartIndex = 0;
        for (int i = 0; i < dataLen; i++) {
            byte bt = encrypted[i];
            boolean isMatchSplit = false;
            if (i == dataLen - 1) {
                // It's the end of data.
                byte[] part = new byte[dataLen - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPrivateKey(part, privateKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            } else if (bt == DEFAULT_SPLIT[0]) {
                // This starts with split[0].
                if (splitLen > 1) {
                    if (i + splitLen < dataLen) {
                        // Not beyond the scope of data
                        for (int j = 1; j < splitLen; j++) {
                            if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                                break;
                            }
                            if (j == splitLen - 1) {
                                // Verify that the last split, without break, indicates that the split segment has been confirmed.
                                isMatchSplit = true;
                            }
                        }
                    }
                } else {
                    // split has only one, and it already matches
                    isMatchSplit = true;
                }
            }
            if (isMatchSplit) {
                byte[] part = new byte[i - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPrivateKey(part, privateKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }
}

Local String Encryption Testing First Gets the Method of Generating the Key to Get the Public Key and the Private Key

        text = "Test encryption RSA";

        keyPair = RSAUtils.generateRSAKeyPair(RSAUtils.DEFAULT_KEY_SIZE);
        // public key
        publicKey = (RSAPublicKey) keyPair.getPublic();
        // private key
        privateKey = (RSAPrivateKey) keyPair.getPrivate();

Public Key Encryption: Getting Encrypted Data

              //Public key encryption
                byte[] encryptBytes = new byte[0];
                try {
                    encryptBytes = RSAUtils.encryptByPublicKeyForSpilt(text.getBytes(), publicKey.getEncoded());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                encryStr = Base64.encode(encryptBytes);
                Log.d("RSA", "Encrypted data:" + encryStr);

Decryption using symmetrical private key after public key encryption

                //Private key decryption
                byte[] decryptBytes = new byte[0];
                try {
                    decryptBytes = RSAUtils.decryptByPrivateKeyForSpilt(Base64.decode(encryStr), privateKey.getEncoded());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                String decryStr = new String(decryptBytes);
                Log.d("RSA", "Decrypted data:" + decryStr);

Private key encryption to obtain encrypted data

                //secret key encryption
                byte[] encryptBytes2 = new byte[0];
                try {
                    encryptBytes2 = RSAUtils.encryptByPrivateKeyForSpilt(text.getBytes(), privateKey.getEncoded());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                encryStr = Base64.encode(encryptBytes2);
                Log.d("RSA", "Encrypted data:" + encryStr);

Decryption using symmetrical public key after private key encryption

                //Public key decryption
                byte[] decryptBytes2 = new byte[0];
                try {
                    decryptBytes2 = RSAUtils.decryptByPublicKeyForSpilt(Base64.decode(encryStr), publicKey.getEncoded());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                decryStr = new String(decryptBytes2);
                Log.d("RSA", "Decrypted data:" + decryStr);

Base64 Tool Class is best used without system. We can implement it by ourselves.

public class Base64 {
    private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
            .toCharArray();

    public static String encode(byte[] data) {
        int start = 0;
        int len = data.length;
        StringBuffer buf = new StringBuffer(data.length * 3 / 2);

        int end = len - 3;
        int i = start;
        int n = 0;

        while (i <= end) {
            int d = ((((int) data[i]) & 0x0ff) << 16)
                    | ((((int) data[i + 1]) & 0x0ff) << 8)
                    | (((int) data[i + 2]) & 0x0ff);

            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append(legalChars[(d >> 6) & 63]);
            buf.append(legalChars[d & 63]);

            i += 3;

            if (n++ >= 14) {
                n = 0;
                buf.append(" ");
            }
        }

        if (i == start + len - 2) {
            int d = ((((int) data[i]) & 0x0ff) << 16)
                    | ((((int) data[i + 1]) & 255) << 8);

            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append(legalChars[(d >> 6) & 63]);
            buf.append("=");
        } else if (i == start + len - 1) {
            int d = (((int) data[i]) & 0x0ff) << 16;

            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append("==");
        }

        return buf.toString();
    }

    private static int decode(char c) {
        if (c >= 'A' && c <= 'Z')
            return ((int) c) - 65;
        else if (c >= 'a' && c <= 'z')
            return ((int) c) - 97 + 26;
        else if (c >= '0' && c <= '9')
            return ((int) c) - 48 + 26 + 26;
        else
            switch (c) {
                case '+':
                    return 62;
                case '/':
                    return 63;
                case '=':
                    return 0;
                default:
                    throw new RuntimeException("unexpected code: " + c);
            }
    }

    /**
     * Decodes the given Base64 encoded String to a new byte array. The byte
     * array holding the decoded data is returned.
     */

    public static byte[] decode(String s) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            decode(s, bos);
        } catch (IOException e) {
            throw new RuntimeException();
        }
        byte[] decodedBytes = bos.toByteArray();
        try {
            bos.close();
            bos = null;
        } catch (IOException ex) {
            System.err.println("Error while decoding BASE64: " + ex.toString());
        }
        return decodedBytes;
    }

    private static void decode(String s, OutputStream os) throws IOException {
        int i = 0;

        int len = s.length();

        while (true) {
            while (i < len && s.charAt(i) <= ' ')
                i++;

            if (i == len)
                break;

            int tri = (decode(s.charAt(i)) << 18)
                    + (decode(s.charAt(i + 1)) << 12)
                    + (decode(s.charAt(i + 2)) << 6)
                    + (decode(s.charAt(i + 3)));

            os.write((tri >> 16) & 255);
            if (s.charAt(i + 2) == '=')
                break;
            os.write((tri >> 8) & 255);
            if (s.charAt(i + 3) == '=')
                break;
            os.write(tri & 255);

            i += 4;
        }
    }
}

In this way, we have simply completed two kinds of encryption methods: public key encryption, private key decryption and private key encryption, public key decryption. This paper is only a simple example of local encryption. Specific and background debugging can be partially referred to.

Use RSA to encrypt, encounter pits, record

1.base64 Tool Class Android Tool Class does not contain some methods. You need to customize it. Refer to Base64 Tool Class in this article.

2. Encryption filling can't be replaced as backstage filling.

 /**
     * Android Encryption Filling Mode
     */
    public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";

    /**
     * java Back-end encryption algorithm RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

Project code

https://github.com/LgSecret/RSADemo.git

Posted by craigerjs on Wed, 08 May 2019 03:40:38 -0700