AES_cbc_encrypt encryption in C corresponds to decryption in java

Preface knowledge:

1.AES (Advanced Encryption Standard) advanced encryption standard, as a block cipher ( divide the plaintext into groups, each group is of equal length , and encrypt one group of data at a time until the entire plaintext is encrypted).

2. In the AES standard specification, the packet length can only be 128 bits , that is, each packet is a multiple of 8 of 16 bytes (8 bits per byte) . The length of the key can be 128 bits, 192 bits or 256 bits, corresponding to , respectively AES128. AES192Different AES256,key lengths have different recommended encryption rounds . From a security perspective, AES256the security is the highest. From a performance perspective, AES128the performance is the highest. The essential reason is that their encryption processing rounds are different.

3. AES is a symmetric encryption algorithm because the same secret key is used for encryption and decryption .

4.AESSupports keys of three lengths: 128 bits, 192 bits, 256 bits. Generally there are at least 4 modes, namely ECB, CBC, CFB, OFB, etc.

 

5.AESWhen the algorithm encrypts plaintext, it does not encrypt the entire plaintext into a whole piece of ciphertext, but splits the plaintext into independent plaintext blocks, each of which is as long as the plaintext block 128bit.

These plaintext blocks are AEScomplexly processed by the encryptor to generate independent ciphertext blocks. These ciphertext blocks are spliced ​​together to form the final AESencryption result.

But there is a problem involved here. If the length of a piece of plaintext is 196bit, if 128bitit is split by each plaintext block, the second plaintext block will only be 64bitinsufficient 128bit. What to do at this time? It is necessary to pad the plaintext block (Padding).

6. Several typical filling methods :

NoPadding: No padding is done, but the plaintext must be an integer multiple of 16 bytes.
PKCS5Padding(Java default): If the plaintext block is less than 16 bytes ( 128bit), the corresponding number of characters is added to the end of the plaintext block, and the value of each byte is equal to the number of missing characters. For example, plain text: {1,2,3,4,5,a,b,c,d,e}, missing 6 bytes, then the completion is {1,2,3,4,5,a,b, c,d,e,6,6,6,6,6,6 }
ISO10126Padding: If the plaintext block is less than 16 bytes ( 128bit), the corresponding number of bytes is added to the end of the plaintext block, and the last character value is equal to the missing character number, other characters are filled with random numbers. For example, plain text: {1,2,3,4,5,a,b,c,d,e}, if 6 bytes are missing, it may be completed as {1,2,3,4,5,a,b ,c,d,e,5,c,3,G,$,6} The
PKCS7Paddingprinciple is PKCS5Paddingsimilar PKCS5Paddingto blocksizethat PKCS7Paddingofblocksize

It should be noted that if AESa certain filling method is used during encryption, the same filling method must be used when decrypting. ZeroPadding(C++ default, Java does not have sub-filling method): If the plaintext is less than 16 bits in Java operation, manual operation is required. Pad zeros and then call NoPadding

AES four working mode principles :

1. ECB mode:

ECB (Electronic Code Book) mode is the simplest block cipher encryption mode . Before encryption, it is divided into several blocks according to the data block size (for example, AES is 128 bits, and the size of each group is the same as the encryption key length ). After that, each block uses the same The key passes individually through the block encryptor. The advantage of this encryption mode is that it is simple and does not require an initialization vector (IV). Each data block is independently encrypted/decrypted, which is conducive to parallel computing and the encryption/decryption efficiency is very high . However, in this mode, all data is encrypted/decrypted using the same key, and no logical operation is performed. The same plaintext results in the same ciphertext, which may lead to the occurrence of a "chosen plaintext attack". ( Earliest and simplest mode )

Advantages: 1. Simple; 2. Conducive to parallel computing; 3. Error will not be diffused; 

Disadvantages: 1. A mode that cannot hide plaintext; 2. Possible active attacks on plaintext; Therefore, this mode is suitable for encrypting small messages

2. CBC mode : reference

CBC ( Cipher Block Chaining ) mode is to first divide the plaintext into several small blocks , and then perform a logical XOR operation on each small block with the initial block or the previous ciphertext segment, and then encrypt it with the key. The first plaintext block is logically XORed with a data block called an initialization vector. This effectively solves the problems exposed by the ECB mode. Even if the two plaintext blocks are the same, the ciphertext blocks obtained after encryption are different . However, the shortcomings are also quite obvious, such as the encryption process is complicated and the efficiency is low. 

Advantages: Not prone to active attacks, better security than ECB, suitable for transmitting long messages, and is the standard for SSL and IPSec. 

Disadvantages: 1. Not conducive to parallel computing; 2. Error propagation; 3. Need to initialize vector IV 

3. CFB mode:

Unlike ECB and CBC modes that can only encrypt block data, CFB mode can convert ciphertext into stream ciphertext. In this encryption mode, since the data encrypted by the block encryptor in the encryption process and decryption process is the ciphertext of the previous block, there is no need to pad even if the length of the plaintext data of this block is not an integer multiple of the data block size. This ensures The data length is the same before and after encryption.

Advantages: 1. Hidden plaintext mode; 2. Convert block cipher to stream mode; 3. Data smaller than the group size can be encrypted and transmitted in a timely manner; 

Disadvantages: 1. Not conducive to parallel computing; 2. Error transmission: damage to one plaintext unit affects multiple units; 3. Unique IV; 

4. OFB mode:

Instead of directly encrypting the plaintext block, the encryption process is to first use a block encryptor to generate a key stream, and then perform a logical XOR operation on the key stream and the plaintext stream to obtain the ciphertext stream.

Advantages: 1. The plaintext mode is hidden; 2. The block cipher is converted into a stream mode; 3. Data smaller than the block size can be encrypted and transmitted in a timely manner; 

Disadvantages: 1. Not conducive to parallel computing; 2. Active attacks on plaintext are possible; 3. Error transmission: damage to one plaintext unit affects multiple units;

AES algorithm principle


General design guidelines for encryption algorithms

Confusion maximizes the complexity of the relationship between ciphertext, plaintext and key, and usually uses non-linear transformation algorithms to achieve maximum confusion.

Diffusion : Every change of one bit in the plaintext or key will maximize the number of bits in the ciphertext. A linear transformation algorithm is usually used to maximize diffusion.

For the Rijndael algorithm, the decryption process is the reverse process of the encryption process. The figure below shows the AES encryption and decryption process. It can be seen from the figure: 1) Each step of the decryption algorithm corresponds to the inverse operation of the encryption algorithm. 2) The order of all encryption and decryption operations is exactly the opposite. It is precisely because of these points (plus the reciprocal operation of each step of the encryption algorithm and the decryption algorithm) that the correctness of the algorithm is guaranteed. Specific reference 1  reference 2

Text (AES_cbc_encrypt encryption in C corresponds to decryption in java)

//C中的加密方式
AES_cbc_encrypt((const unsigned char*)in.data(),
                    (unsigned char*)out.data(),
                    len,
                    &aes,
                    (unsigned char*)ivec.data(),
                    AES_ENCRYPT);

AES_cbc_encrypt: This is the CBC encryption method in AES in C++. It can encrypt input data of any length. If the input data is not an integer multiple of 16 bytes, the function will use ZeroPadding to automatically fill the input data, and then Encrypt. Then I checked and found that there is no ZeroPadding in Java. ZeroPadding is manually implemented and then NoPadding is called . My specific implementation:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName AESCBCUtil
 * @Description TODO
 * @Author wanghaha
 * @Date 2023/3/1
 **/
public class AESCBCUtil {


    /*
     * @Description: 满足pc的加密方式 生成128位
     * @Author: wanghaha
     * @Date: 2023/3/1
     * @param: data
     * @param: iv
     * @return: java.lang.String
     **/
    public static String encrypt128(String data, String iv) {
        byte[] key = new byte[16];
        for (int i = 0; i < 16; i++) {
            key[i] = (byte) (29 + i * 3);
        }
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            byte[] dataBytes = data.getBytes();
            byte[] plaintext = new byte[128];
            //填充
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            //设置偏移量参数
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            byte[] encryped = cipher.doFinal(plaintext);

            return DatatypeConverter.printBase64Binary(encryped);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /* 加密 正常长度
     * @Description:
     * @Author: wanghaha
     * @Date: 2023/3/1
     * @param: null
     * @return: null
     **/
    public static String encrypt(String data, String iv) {
        byte[] key = new byte[16];
        for (int i = 0; i < 16; i++) {
            key[i] = (byte) (29 + i * 3);
        }
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            byte[] dataBytes = data.getBytes();
            int blockSize = cipher.getBlockSize();
            int length = dataBytes.length;
            //计算需填充长度
            if (length % blockSize != 0) {
                length = length + (blockSize - (length % blockSize));
            }
            byte[] plaintext = new byte[length];
            //填充
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            //设置偏移量参数
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            byte[] encryped = cipher.doFinal(plaintext);

            return DatatypeConverter.printBase64Binary(encryped);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //解密
    public static String desEncrypt(String data, String iv) {
        byte[] key = new byte[16];
        for (int i = 0; i < 16; i++) {
            key[i] = (byte) (29 + i * 3);
        }
        try {
            byte[] encryp = DatatypeConverter.parseBase64Binary(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            byte[] original = cipher.doFinal(encryp);
            //因为pc端使用加密的是zeroPadding: 后补0的Nopadding方式 且 都是字符unsigned限制的 char正数 可以通过判断第一个0的位置判断字符长度
            int end=0;
            while(end < original.length && original[end] != 0){
                end++;
            }
            return new String(original,0, end);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //测试
    public static void main(String[] args) {
        String data = "232342342342342";
        String iv = "2334022720230227";
        String encrypt= encrypt(data ,iv);

        String desencrypt = desEncrypt(encrypt,iv);
        System.out.println("长加密后:"+encrypt);
        System.out.println("长解密后:"+desencrypt);


    }
}

Solutions to bugs encountered:

bug1 : When doing AES decryption , I encountered the error "Given final block not properly padded. Such issues can arise if a bad key is used during decryption", which means that the keys corresponding to encryption and encryption are different .

bug2 : When using Java's Cipher class for AES encryption, an error is reported: IllegalBlockSizeException: Input length not multiple of 16 bytes 

The error code Cipher cipher = Cipher.getInstance("AES/ CBC /NoPadding")

If the padding method is Nopadding, then the encrypted plaintext must be a multiple of 8, or the key must be 16 bits.
The length of the content to be encrypted must be a multiple of 16, if it is not a multiple of 16

Summary: Although Java supports the CBC mode of AES, the filling method does not support ZeroPadding.

Principle: ZeroPadding is to use '\0' to fill the encrypted content to an integer multiple of BlockSize (CBC is generally 16), and then fill it with NoPadding.

Guess you like

Origin blog.csdn.net/qq_40453972/article/details/129304154