Understanding of AES encryption algorithm and Java implementation __ Coding

Source: Internet
Author: User
Tags base64 decrypt modulus


First, the basic concept of AES algorithm



AES is the abbreviation of Advanced Encryption Standard (Advanced encryption Standard), AES has the following three characteristics.



1 AES is symmetric encryption algorithm



The concept of symmetric encryption, Baidu Encyclopedia is this explanation: in the symmetric encryption algorithm, the data originator of the plaintext (raw data) and encryption key together through a special encryption algorithm processing, make it into a complex encrypted ciphertext sent out. After receiving the ciphertext, if you want to interpret the original text, you need to use the encryption key and the same algorithm of the inverse algorithm to decrypt the ciphertext, in order to enable it to revert to readable plaintext. In the symmetric encryption algorithm, only one key is used, both the sender and the receiver use the key to encrypt and decrypt the data, which requires the decryption party to know the encryption key beforehand. There are two key points: 1, the encryption side, the decryption party using the same key, 2, encryption algorithm and decryption algorithm for each other inverse algorithm. Example: 123456-->234567 encryption key is 1, the encryption algorithm is each +;234567-->123456 decryption key is also 1, the decryption algorithm is each-; the encryption Algorithm (+) and decryption algorithm (-) mutually inverse algorithm, This encryption algorithm is called symmetric encryption.



2, AES belongs to block encryption



AES needs to divide the plaintext into several blocks of fixed length before encryption, then encrypt each block of plaintext, and finally spell it as a complete encrypted string. Block encryption to fill in the last piece, the encryption side, the decryption side to use the same padding fill method.



3, AES key, initial vector, encryption mode, filling mode



We often say that the AES-128, AES-192, AES-256 Three encryption methods, the number represents the key length, such as AES-128 said that the key is 128 bits, that is, 16 byte length of the string. JDK currently supports only AES-128 encryption, which means that the incoming key must be a string of length 16.



The initial vector IV (initialization vector), which uses other encryption modes other than the ECB, needs to pass in an initial vector, which is equal to the block size and the AES block size is 128bit, so the length of the IV is 16 bytes and the initial vector can enhance the algorithm strength.



Encryption Mode (Cipher mode) has CBC, ECB, CTR, ofb, CFB five species.



The Padding method (Padding) determines the content that the last block needs to be populated, filled with pkcs5padding, pkcs7padding, nopadding three, but the JDK only provides pkcs5padding, nopadding two species, When the fill is pkcs5padding, the last block needs to be populated with χ bytes, and the fill value is χ; When the fill is nopadding, the last block fills the contents of the programmer's own decision, usually populated by 0.






Two, the Java implementation of AES algorithm





package com.xi.liuliu.topnews.utils;

import android.util.Log;

import java.io.UnsupportedEncodingException;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/ **
 * Created by zhangxiaobei on 2017/1/16.
 * /

public class AesEncryptUtil {
    private static final String TAG = "AesEncryptUtil";
    private static final String UTF8 = "UTF-8";
    private static final String AES = "AES";
    private static final String AES_CBC_PKCS5_PADDING = "AES / CBC / PKCS5Padding";
    private static final String AES_CBC_NO_PADDING = "AES / CBC / NoPadding";

    / **
     * The JDK only supports AES-128 encryption, that is, the key length must be 128 bits; the parameter is the key key, which is supplemented with "0" when the key length is less than 16 characters, and the first 16 bits are truncated when the key length is greater than 16 characters
     ** /
    private static SecretKeySpec create128BitsKey (String key) {
        if (key == null) {
            key = "";
        }
        byte [] data = null;
        StringBuffer buffer = new StringBuffer (16);
        buffer.append (key);
        // less than 16 followed by 0
        while (buffer.length () <16) {
            buffer.append ("0");
        }
        // greater than 16, intercept the first 16 characters
        if (buffer.length ()> 16) {
            buffer.setLength (16);
        }
        try {
            data = buffer.toString (). getBytes (UTF8);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace ();
        }
        return new SecretKeySpec (data, AES);
    }

    / **
     * Create a 128-bit offset. When the length of iv is less than 16, add 0 to the back, greater than 16, and intercept the first 16 characters.
     *
     * @param iv
     * @return
     * /
    private static IvParameterSpec create128BitsIV (String iv) {
        if (iv == null) {
            iv = "";
        }
        byte [] data = null;
        StringBuffer buffer = new StringBuffer (16);
        buffer.append (iv);
        while (buffer.length () <16) {
            buffer.append ("0");
        }
        if (buffer.length ()> 16) {
            buffer.setLength (16);
        }
        try {
            data = buffer.toString (). getBytes (UTF8);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace ();
        }
        return new IvParameterSpec (data);
    }

    / **
     * When the filling method is Pkcs5Padding, the last block needs to be filled with χ bytes, and the value to be filled is χ, that is, the content to be filled is determined by the JDK
     *
     * @param srcContent
     * @param password
     * @param iv
     * @return
     * /
    public static byte [] aesCbcPkcs5PaddingEncrypt (byte [] srcContent, String password, String iv) {
        SecretKeySpec key = create128BitsKey (password);
        IvParameterSpec ivParameterSpec = create128BitsIV (iv);
        try {
            Cipher cipher = Cipher.getInstance (AES_CBC_PKCS5_PADDING);
            cipher.init (Cipher.ENCRYPT_MODE, key, ivParameterSpec);
            byte [] encryptedContent = cipher.doFinal (srcContent);
            return encryptedContent;
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }


    public static byte [] aesCbcPkcs5PaddingDecrypt (byte [] encryptedContent, String password, String iv) {
        SecretKeySpec key = create128BitsKey (password);
        IvParameterSpec ivParameterSpec = create128BitsIV (iv);
        try {
            Cipher cipher = Cipher.getInstance (AES_CBC_PKCS5_PADDING);
            cipher.init (Cipher.DECRYPT_MODE, key, ivParameterSpec);
            byte [] decryptedContent = cipher.doFinal (encryptedContent);
            return decryptedContent;
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }

    / **
     * When the padding method is NoPadding, the padding content of the last block is determined by the programmer, usually 0.
     * The length of the plaintext encrypted by AES / CBC / NoPadding must be an integer multiple of 16. When the plaintext length does not satisfy 16, the programmer must expand to an integer multiple of 16.
     *
     * @param sSrc
     * @param aesKey
     * @param aesIV
     * @return
     * /
    public static byte [] aesCbcNoPaddingEncrypt (byte [] sSrc, String aesKey, String aesIV) {
        // When the length of the encrypted data is not an integer multiple of 16, the original data is padded with zeros until the length meets the integer multiple of 16.
        int len = sSrc.length;
        // Calculate the length after padding
        while (len% 16! = 0) len ++;
        byte [] result = new byte [len];
        // padded 0 at the end
        for (int i = 0; i <len; ++ i) {
            if (i <sSrc.length) {
                result [i] = sSrc [i];
            } else {
                // fill character 'a'
                // result [i] = 'a';
                result [i] = 0;
            }
        }
        SecretKeySpec skeySpec = create128BitsKey (aesKey);
        // Using CBC mode requires an initial vector iv, which can increase the strength of the encryption algorithm
        IvParameterSpec iv = create128BitsIV (aesIV);
        Cipher cipher = null;
        try {
            // Algorithm / pattern / complement method
            cipher = Cipher.getInstance (AES_CBC_NO_PADDING);
            cipher.init (Cipher.ENCRYPT_MODE, skeySpec, iv);
        } catch (Exception e) {
            e.printStackTrace ();
            Log.i (TAG, "aesCbcNoPaddingEncrypt Exception");
        }
        byte [] encrypted = null;
        try {
            encrypted = cipher.doFinal (result);
        } catch (Exception e) {
            e.printStackTrace ();
            Log.i (TAG, "aesCbcNoPaddingEncrypt Exception");
        }
        return encrypted;
    }

    public static byte [] aesCbcNoPaddingDecrypt (byte [] sSrc, String aesKey, String aesIV) {
        SecretKeySpec skeySpec
= create128BitsKey (aesKey);
         IvParameterSpec iv = create128BitsIV (aesIV);
         try {
             Cipher cipher = Cipher.getInstance (AES_CBC_NO_PADDING);
             cipher.init (Cipher.DECRYPT_MODE, skeySpec, iv);
             byte [] decryptContent = cipher.doFinal (sSrc);
             return decryptContent;
         } catch (Exception ex) {
             Log.i (TAG, "aesCbcNoPaddingDecrypt Exception");
         }
         return null;
     }
}




The concept and realization of Base64 transcoding



AES encrypted return is byte[], print out will show a lot of garbled, in order to improve readability, the general will byte[] into Base64 encoding, Base64 is not a cryptographic algorithm, is a coding method, base64 encoding means including a-z,a-z,0-9,+,/these characters, Encoding byte[] is converted to a string containing the above characters. Base64encoder Java Implementation:





package com.xi.liuliu.topnews.utils;

/**
 * Created by zhangxiaobei on 2017/1/9.
 */

public class Base64Encoder {
    private static final byte[] encodingTable = {
            (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
            (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
            (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
            (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
            (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
            (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
            (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
            (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
            (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
            (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
            (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2',
            (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
            (byte) '8', (byte) '9', (byte) '+', (byte) '/'
    };
    private static final byte[] decodingTable;
    static {
        decodingTable = new byte[128];
        for (int i = 0; i < 128; i++) {
            decodingTable[i] = (byte) -1;
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            decodingTable[i] = (byte) (i - 'A');
        }
        for (int i = 'a'; i <= 'z'; i++) {
            decodingTable[i] = (byte) (i - 'a' + 26);
        }
        for (int i = '0'; i <= '9'; i++) {
            decodingTable[i] = (byte) (i - '0' + 52);
        }
        decodingTable['+'] = 62;
        decodingTable['/'] = 63;
    }
    public static byte[] encode(byte[] data) {
        byte[] bytes;
        int modulus = data.length % 3;
        if (modulus == 0) {
            bytes = new byte[(4 * data.length) / 3];
        } else {
            bytes = new byte[4 * ((data.length / 3) + 1)];
        }
        int dataLength = (data.length - modulus);
        int a1;
        int a2;
        int a3;
        for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) {
            a1 = data[i] & 0xff;
            a2 = data[i + 1] & 0xff;
            a3 = data[i + 2] & 0xff;
            bytes[j] = encodingTable[(a1 >>> 2) & 0x3f];
            bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f];
            bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f];
            bytes[j + 3] = encodingTable[a3 & 0x3f];
        }
        int b1;
        int b2;
        int b3;
        int d1;
        int d2;
        switch (modulus) {
            case 0: /* nothing left to do */
                break;
            case 1:
                d1 = data[data.length - 1] & 0xff;
                b1 = (d1 >>> 2) & 0x3f;
                b2 = (d1 << 4) & 0x3f;
                bytes[bytes.length - 4] = encodingTable[b1];
                bytes[bytes.length - 3] = encodingTable[b2];
                bytes[bytes.length - 2] = (byte) '=';
                bytes[bytes.length - 1] = (byte) '=';
                break;
            case 2:
                d1 = data[data.length - 2] & 0xff;
                d2 = data[data.length - 1] & 0xff;
                b1 = (d1 >>> 2) & 0x3f;
                b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
                b3 = (d2 << 2) & 0x3f;
                bytes[bytes.length - 4] = encodingTable[b1];
                bytes[bytes.length - 3] = encodingTable[b2];
                bytes[bytes.length - 2] = encodingTable[b3];
                bytes[bytes.length - 1] = (byte) '=';
                break;
        }
        return bytes;
    }
    public static byte[] decode(byte[] data) {
        byte[] bytes;
        byte b1;
        byte b2;
        byte b3;
        byte b4;
        data = discardNonBase64Bytes(data);
        if (data[data.length - 2] == '=') {
            bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
        } else if (data[data.length - 1] == '=') {
            bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
        } else {
            bytes = new byte[((data.length / 4) * 3)];
        }
        for (int i = 0, j = 0; i < (data.length - 4); i += 4, j += 3) {
            b1 = decodingTable[data[i]];
            b2 = decodingTable[data[i + 1]];
            b3 = decodingTable[data[i + 2]];
            b4 = decodingTable[data[i + 3]];
            bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
            bytes[j + 2] = (byte) ((b3 << 6) | b4);
        }
        if (data[data.length - 2] == '=') {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
        } else if (data[data.length - 1] == '=') {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            b3 = decodingTable[data[data.length - 2]];
            bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
        } else {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            b3 = decodingTable[data[data.length - 2]];
            b4 = decodingTable[data[data.length - 1]];
            bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
            bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
        }
        return bytes;
    }
    public static byte[] decode(String data) {
        byte[] bytes;
        byte b1;
        byte b2;
        byte b3;
        byte b4;
        data = discardNonBase64Chars(data);
        if (data.charAt(data.length() - 2) == '=') {
            bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
        } else if (data.charAt(data.length() - 1) == '=') {
            bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
        } else {
            bytes = new byte[((data.length() / 4) * 3)];
        }
        for (int i = 0, j = 0; i < (data.length() - 4); i += 4, j += 3) {
            b1 = decodingTable[data.charAt(i)];
            b2 = decodingTable[data.charAt(i + 1)];
            b3 = decodingTable[data.charAt(i + 2)];
            b4 = decodingTable[data.charAt(i + 3)];
            bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
            bytes[j + 2] = (byte) ((b3 << 6) | b4);
        }
        if (data.charAt(data.length() - 2) == '=') {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
        } else if (data.charAt(data.length() - 1) == '=') {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            b3 = decodingTable[data.charAt(data.length() - 2)];
            bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
        } else {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            b3 = decodingTable[data.charAt(data.length() - 2)];
            b4 = decodingTable[data.charAt(data.length() - 1)];
            bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
            bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
            bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
        }
        return bytes;
    }
    private static byte[] discardNonBase64Bytes(byte[] data) {
        byte[] temp = new byte[data.length];
        int bytesCopied = 0;
        for (int i = 0; i < data.length; i++) {
            if (isValidBase64Byte(data[i])) {
                temp[bytesCopied++] = data[i];
            }
        }
        byte[] newData = new byte[bytesCopied];
        System.arraycopy(temp, 0, newData, 0, bytesCopied);
        return newData;
    }
    private static String discardNonBase64Chars(String data) {
        StringBuffer sb = new StringBuffer();
        int length = data.length();
        for (int i = 0; i < length; i++) {
            if (isValidBase64Byte((byte) (data.charAt(i)))) {
                sb.append(data.charAt(i));
            }
        }
        return sb.toString();
    }
    private static boolean isValidBase64Byte(byte b) {
        if (b == '=') {
            return true;
        } else if ((b < 0) || (b >= 128)) {
            return false;
        } else if (decodingTable[b] == -1) {
            return false;
        }
        return true;
    }


}

The process of sending encrypted data that we often use is:


Front end, Mobile end:



1 AES encryption get encrypted data



2 BASE64 encoded encrypted data



3 Send BASE64 encoded data



Back end:



4 BASE64 decoding data



5 AES Decryption data



6 Storing raw data






Reference:



http://blog.csdn.net/uikoo9/article/details/27983071



http://blog.csdn.net/qq_18870023/article/details/52183755


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.