Java AES encryption test knife


In the process of java development, we often need to encrypt data, such as sound, sensitive information and so on. We usually use MD5 encryption, SHA encryption, DES encryption, AES encryption, etc. Today we'll look at AES encryption.

Source of the problem

In the project, the code was well written, and there was no problem in the local test. It was packaged and released, went home happily, and went to the company the next day to find the encrypted data. The decryption failed when downloading. What's the matter? What's wrong? Sweat flowed directly. A casual thought: what's the difference between windows and linux? So I began to investigate and have the following summary.

resolvent

Method 1

The code is as follows:

    private void encryptAES1(String key, ByteArrayInputStream inputStream, ByteArrayOutputStream outputStream) throws IOException {
        CipherInputStream cipherInputStream = null;
        try{
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(128, new SecureRandom(key.getBytes(StandardCharsets.UTF_8)));
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyEncoded = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyEncoded, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// Decrypt Cipher.DECRYPT_MODE
            cipherInputStream = new CipherInputStream(inputStream, cipher);
            byte[] bytes = new byte[64];
            int numBytes;
            while ((numBytes = cipherInputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, numBytes);
            }
        }catch (NoSuchPaddingException | NoSuchAlgorithmException | IOException | InvalidKeyException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            if (null != cipherInputStream){
                cipherInputStream.close();
            }
        }
    }

This method can be encrypted and decrypted in Windows system. However, in linux system, encryption and decryption will fail. The reason is that different windows and linux kernels produce different random numbers, which leads to the failure of encryption and decryption of linux system. Refer to method 2 for solutions

Method 2

The code is as follows:

    private void encryptAES2(String key, ByteArrayInputStream inputStream, ByteArrayOutputStream outputStream) throws IOException {
        CipherInputStream cipherInputStream = null;
        try{
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(key.getBytes(StandardCharsets.UTF_8));
            keyGenerator.init(128, secureRandom);
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyEncoded = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyEncoded, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);//Decrypt Cipher.DECRYPT_MODE
            cipherInputStream = new CipherInputStream(inputStream, cipher);
            byte[] bytes = new byte[64];
            int numBytes;
            while ((numBytes = cipherInputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, numBytes);
            }
        }catch (NoSuchPaddingException | NoSuchAlgorithmException | IOException | InvalidKeyException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            if (null != cipherInputStream){
                cipherInputStream.close();
            }
        }
    }

This method can solve the differences of encryption algorithms in different systems, and can successfully realize the encryption and decryption of windows and linux systems. However, through code detection, it is found that SHA1PRNG in [SecureRandom.getInstance("SHA1PRNG");] is at code risk. Through searching the data, it is found that it can be replaced with NativePRNG, but the operation fails after the code is written. After consulting the company's predecessors, we came to the scheme of method 3.

Method 3

The code is as follows:

    private void encryptAES3(String key, ByteArrayInputStream inputStream, ByteArrayOutputStream outputStream) throws IOException {
        CipherInputStream cipherInputStream = null;
        try{
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(key.getBytes(StandardCharsets.UTF_8));
            byte[] keyBytes = new byte[16];
            System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// Cipher.DECRYPT_MODE
            cipherInputStream = new CipherInputStream(inputStream, cipher);
            byte[] bytes = new byte[64];
            int numBytes;
            while ((numBytes = cipherInputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, numBytes);
            }
        }catch (NoSuchPaddingException | NoSuchAlgorithmException | IOException | InvalidKeyException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            if (null != cipherInputStream){
                cipherInputStream.close();
            }
        }
    }

This method removes random numbers and uses the MessageDigest class to generate keys with the same number of bits as the KeyGenerator. There are no code risks or defects that have not been found. If friends passing by know, please leave a message.

supplement

  • In the above three methods, the first parameter of keyGenerator.init(param1, param2) method is the number of bits to generate random numbers, which can be 128 (16 bytes), 192 (24 bytes) and 256 (32 bytes). If the data to be encrypted is less than 16 bytes, 16 bytes of encrypted data will be generated after encryption; Equal to 16 bytes of data, 32 bytes of encrypted data will be generated after encryption. Therefore, in method 3, [byte[] keyBytes =newbyte[16];] is 16.
  • Suggestion: if it is necessary to splice the encrypted data and decrypt the whole file, such as real-time sound data, large text messages (segmented encryption), the best encrypted data is a multiple of 16. After encryption, subtract the last 16 bytes before splicing (the last data encryption does not need to be reduced by 16).

summary

Here is a brief introduction to AES encryption. Why not explain the principles and differences of DES, AES and MD5 encryption in detail, because I don't understand their principles enough 🤦. Interested partners can search online for their principles and differences.

Keywords: Java Linux Windows

Added by shinephp on Sat, 09 Oct 2021 21:24:36 +0300