Completely bid farewell to the code copy of encryption and decryption module – detailed explanation of JCE core cpiher

premise

javax. crypto. Cipher, translated as a password, is actually called a cipher, which is more suitable. Cipher is the core of JCA (Java cryptographic extension) and provides encryption and decryption functions based on a variety of encryption and decryption algorithms. Before we knew about cipher, we always need to copy the code everywhere when completing some modules that need encryption and decryption. Even some wrong usage has been copied countless times. After stepping on the hole, we have to copy the code to fill the hole. Why not try to understand cipher and use it rationally?

Some knowledge supplement of cipher initialization transformation

Transformation mode transformation is generally composed of three parts. The format is: algorithm / mode / padding. For example: DES / CBC / pkcs5padding.

algorithm

The algorithm refers to the English string of the name of the specific encryption and decryption algorithm, such as "SHA-256", "RSA", etc. the implementation principle of the specific algorithm is not expanded here.

Working mode

In fact, the working mode is mainly for block cipher. Block cipher is a sequence of numbers (plaintext numbers for short) encoded and represented by plaintext messages, which is divided into groups with a growth of n (you can see the vector with a growth of n), and each group is transformed into a sequence of output numbers of equal length (ciphertext numbers for short) under the control of the key. The emergence of the working mode is mainly based on the following reasons:

In essence, the working mode is a technology to enhance the cryptographic algorithm or adapt the algorithm to specific applications, such as applying block cipher to sequences or data streams composed of data blocks. At present, it mainly includes the following five working modes defined by NIST:

The above five working modes can be used for any block cipher including 3DES and AES. The choice of which working mode needs to be analyzed in combination with the actual situation.

Fill mode

Padding means that the block encryption algorithm requires the original data length to be an integer multiple of the fixed block size. If the original data length is greater than the fixed block size, the fixed block needs to be filled with data until the data of the whole block is complete. For example, we agree that the length of the block is 128, but the length of the original text to be encrypted is 129, so it needs to be divided into two encrypted blocks. The second encrypted block needs to be filled with 127 length data, and the filling mode determines how to fill the data.

Filling the data during encryption and removing the filling during decryption are the key factors to be considered by both sides of communication. Filling in the original text is mainly based on the following reasons:

There are at least five common filling methods. Most of the filling methods used in different programming languages to realize encryption and decryption come from these methods or their variants. The following five fill patterns are excerpted from the paper in resources:

1. The filling data is the length of the filling byte sequence

In this filling method, the filling string is composed of a byte sequence, and each byte fills the length of the byte sequence. Assuming that the block length is 8 and the original data length is 9, the number of filled bytes is equal to 0x07; If the plaintext data length is an integer multiple of 8, the number of padding bytes is 0x08. The fill string is as follows:

2. Add 0x00 after the filling data is 0x 80

In this filling method, the first byte of the filling string is 0x80, and each subsequent byte is 0x00. Assuming that the block length is 8 and the original data length is 9 or an integer multiple of 8, the filling string is as follows:

3. The last byte of the padding data is the length of the padding byte sequence

In this filling method, the last byte of the filling string is the length of the sequence, and the previous byte can be 0x00 or a random byte sequence. Assuming that the block length is 8 and the original data length is 9 or an integer multiple of 8, the filling string is as follows:

4. The filled data is blank

In this filling method, each byte of the filling string is the number of bytes corresponding to the space 0x20. Assuming that the block length is 8 and the original data length is 9 or an integer multiple of 8, the filling string is as follows:

5. The filling data is 0x00

In this padding method, each byte of the padding string is 0x00. Assuming that the block length is 8 and the original data length is 9 or an integer multiple of 8, the filling string is as follows:

Transformation summary

Some details of cipher supported by sunjce provider are as follows:

The padding (cipher) supported by Java natively is summarized as follows:

Other padding needs to be provided by a third-party provider.

All public methods of cipher

Seven main public attributes of cipher

Cipher's main methods

GetInstance method

Cipher provides three static factory methods getInstance to build its instance. The three methods are as follows:

public static final Cipher getInstance(String transformation)
                                throws NoSuchAlgorithmException,NoSuchPaddingException

public static final Cipher getInstance(String transformation,String provider)
                                throws NoSuchAlgorithmException,NoSuchProviderException,Provider provider)
                                throws NoSuchAlgorithmException,NoSuchPaddingException

Transformation, here called transformation (mode), is the core parameter. See the analysis in the previous section. In addition, there are two factory methods that require you to pass in Java security. The full class name or instance of the provider. Because cipher wants to obtain the implementation of the specified transformation mode from the corresponding provider, the first factory method has only one parameter transformation, which will be obtained from all ready-made Java security. The provider takes out the first service that meets the transformation and instantiates cipherspi from it (understand that cipher delegates to the internally held cipherspi instance to complete the specific encryption and decryption functions). In fact, the initialization of the cipher instance must depend on the transformation mode and provider.

Init method

The init method has eight variants. This method is mainly used to initialize cipher.

//额外参数是Key(密钥)
public final void init(int opmode,Key key)
                throws InvalidKeyException

//额外参数是Key(密钥)和SecureRandom(随机源)
public final void init(int opmode,Key key,SecureRandom random)
                throws InvalidKeyException

//额外参数是Key(密钥)和AlgorithmParameterSpec(算法参数透明定义)
public final void init(int opmode,AlgorithmParameterSpec params)
                throws InvalidKeyException,InvalidAlgorithmParameterException 

//额外参数是Key(密钥)、AlgorithmParameterSpec(算法参数透明定义)和SecureRandom(随机源)
public final void init(int opmode,AlgorithmParameterSpec params,SecureRandom random)
                throws InvalidKeyException,InvalidAlgorithmParameterException

//额外参数是Key(密钥)、AlgorithmParameters(算法参数)
public final void init(int opmode,AlgorithmParameters params)
                throws InvalidKeyException,InvalidAlgorithmParameterException

//额外参数是Key(密钥)、AlgorithmParameters(算法参数)、SecureRandom(随机源)
public final void init(int opmode,AlgorithmParameters params,SecureRandom random)
                    throws InvalidKeyException,InvalidAlgorithmParameterException

//额外参数是Certificate(证书)
public final void init(int opmode,Certificate certificate)
                throws InvalidKeyException

//额外参数是Certificate(证书)、SecureRandom(随机源)
public final void init(int opmode,Certificate certificate,SecureRandom random)
                throws InvalidKeyException

Opmode is a required parameter and the optional value is encrypt_ MODE、DECRYPT_ MODE、WRAP_ Mode and unwrap_ MODE。 If the key type parameter is not asymmetric encryption, the corresponding type is secretkey. If it is asymmetric encryption, it can be publickey or privatekey. Securerandom is a random source, because some algorithms need different encryption results every time. At this time, it needs to rely on the system or the incoming random source. Some algorithms that require the same encryption and decryption results every time, such as AES, cannot use this parameter (or a fixed random source seed must be specified). Certificate is a certificate implementation with a key. The algorithm parameters mainly include IV (initialization vector) and so on.

Wrap method and unwrap method

The wrap method is used to wrap a key.

public final byte[] wrap(Key key)
                  throws IllegalBlockSizeException,InvalidKeyException

When using the wrap method, it should be noted that cipher's opmode should be initialized to wrap_ MODE。

The unwrap method is used to unwrap a key.

public final Key unwrap(byte[] wrappedKey,String wrappedKeyAlgorithm,int wrappedKeyType)
                 throws InvalidKeyException,NoSuchAlgorithmException

When using the unwrap method, it should be noted that cipher's opmode should be initialized to unwrap_ Mode. When calling the unwrap method, you need to specify the algorithm that wraps the key and the type of key.

In fact, wrap and unwrap are mutually inverse operations:

public enum EncryptUtils {

    /**
     * 单例
     */
    SINGLETON;

    private static final String SECRECT = "passwrod";

    public String wrap(String keyString) throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //初始化密钥生成器,指定密钥长度为128,指定随机源的种子为指定的密钥(这里是"passward")
        keyGenerator.init(128,new SecureRandom(SECRECT.getBytes()));
        SecretKey secretKey = keyGenerator.generateKey();
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),"AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.WRAP_MODE,secretKeySpec);
        SecretKeySpec key = new SecretKeySpec(keyString.getBytes(),"AES");
        byte[] bytes = cipher.wrap(key);
        return Hex.encodeHexString(bytes);
    }

    public String unwrap(String keyString) throws Exception {
        byte[] rawKey = Hex.decodeHex(keyString);
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //初始化密钥生成器,指定密钥长度为128,指定随机源的种子为指定的密钥(这里是"passward")
        keyGenerator.init(128,"AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.UNWRAP_MODE,secretKeySpec);
        SecretKey key = (SecretKey) cipher.unwrap(rawKey,"AES",Cipher.SECRET_KEY);
        return new String(key.getEncoded());
    }

    public static void main(String[] args) throws Exception {
        String wrapKey = EncryptUtils.SINGLETON.wrap("doge");
        System.out.println(wrapKey);
        System.out.println(EncryptUtils.SINGLETON.unwrap(wrapKey));
    }
}

The above example is to wrap and unpack the key through AES, call the main method, and output:

77050742188d4b97a1d401db902b864d
doge

Update method

There are several variants of the update method, which have almost the same meaning:

public final byte[] update(byte[] input)

public final byte[] update(byte[] input,int inputOffset,int inputLen)

public final int update(byte[] input,int inputLen,byte[] output)
                 throws ShortBufferException    

public final int update(ByteBuffer input,ByteBuffer output)
                 throws ShortBufferException

The update method is mainly used for partial encryption or partial decryption. The encryption or decryption depends on the opmode during cipher initialization. Even though it has multiple variants, the routine is the same: depending on an input buffer (with data to be encrypted or decrypted), the return value or parameter is an output buffer, and some additional parameters can control the data segment of encryption or decryption operation through offset and length. After partial encryption or decryption, you must call @ H_ 291_ 419@Cipher #The dofinal () method to end the encryption or decryption operation.

Dofinal method

There are also several variants of the dofinal method:

/**
 * 结束多部分加密或者解密操作。
 * 此方法需要在update调用链执行完毕之后调用,返回的结果是加密或者解密结果的一部分。
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final byte[] doFinal()
                     throws IllegalBlockSizeException,BadPaddingException

/**
 * 结束多部分加密或者解密操作。
 * 此方法需要在update调用链执行完毕之后调用,传入的output作为缓冲区接收加密或者解密结果的一部分。
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final int doFinal(byte[] output,int outputOffset)
                  throws IllegalBlockSizeException,ShortBufferException,BadPaddingException                         

/**
 * 结束单部分加密或者解密操作。
 * 此方法接收需要加密或者解密的完整报文,返回处理结果
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final byte[] doFinal(byte[] input)
                     throws IllegalBlockSizeException,BadPaddingException

/**
 * 结束单部分或者多部分加密或者解密操作。
 * 参数inputOffset为需要加解密的报文byte数组的起始位置,inputLen为需要加密或者解密的字节长度
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final byte[] doFinal(byte[] input,int inputLen)
                     throws IllegalBlockSizeException,BadPaddingException      

/**
 * 结束单部分或者多部分加密或者解密操作。
 * 参数inputOffset为需要加解密的报文byte数组的起始位置,inputLen为需要加密或者解密的字节长度,output用于接收加解密的结果
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final int doFinal(byte[] input,byte[] output)
                  throws ShortBufferException,IllegalBlockSizeException,BadPaddingException                                                         

/**
 * 结束单部分或者多部分加密或者解密操作。
 * 参数inputOffset为需要加解密的报文byte数组的起始位置,inputLen为需要加密或者解密的字节长度,
 * output用于接收加解密的结果,outputOffset用于设置output的起始位置
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final int doFinal(byte[] input,byte[] output,int outputOffset)
                  throws ShortBufferException,BadPaddingException 
/**
 * 结束单部分或者多部分加密或者解密操作。
 * 参数input为输入缓冲区,output为输出缓冲区
 * 此方法正常调用结束之后Cipher会重置为初始化状态。
 */
public final int doFinal(ByteBuffer input,ByteBuffer output)
                  throws ShortBufferException,BadPaddingException                                              

The main function of dofinal is to end single part or multi part encryption or decryption operations. Single part encryption or decryption is applicable to the case where the length of the message to be processed is short without blocking. At this time, @ h is directly used_ 291_ 419@byte [] dofinal (byte [] input) method. Multi part encryption or decryption is applicable to the case where the message to be processed is long and needs to be partitioned. At this time, @ h needs to be called many times_ 291_ 419@update Method variant part of the block encryption and decryption, the last call @H_ 291_ 419@doFinal End of partial encryption and decryption operation of method variant. For example, if the size of the processing block is 8 and the actual length of the message to be encrypted is 23, it needs to be encrypted in three blocks. The messages with the length of 8 in the first two blocks need to call Update for partial encryption. The results of partial encryption can be obtained from the return value of update, and the messages with the length of the last 7 (in fact, it is generally filled with the length of 8) call dofinal for encryption, End the encryption operation of the whole part. In addition, it is worth noting that as long as cipher calls any @ h normally_ 291_ 419@doFinal Variant method (no exceptions are thrown in the process), then cipher will be reset to the initialization state and can continue to be used. This reusable feature can reduce the performance loss of creating cipher instances.

Updateadd method

First, add means additional authentication data. Updateadd also has three method variants:

public final void updateAAD(byte[] src)

public final void updateAAD(byte[] src,int offset,int len)

public final void updateAAD(ByteBuffer src)

Its method variants only rely on one input buffer with additional authentication data, which is generally used in @ H_ 291_ 419@GCM Or @ h_ 291_ 419@CCM Encryption and decryption algorithm. If this method is used, its call must be in cipher's @ H_ 291_ 419@update And @ h_ 291_ 419@doFinal Before the variant method is called, it is very simple to understand. Authentication must be done before the actual encryption and decryption operation. At present, @ h_ 291_ 419@updateADD There are few data, and the author has not practiced looking for that in the production environment, so I don't do analysis.

Other methods

Other methods are mainly getter methods, which are used to obtain information about cipher.

Cipher workflow

Draw a diagram below to analyze cipher's workflow in detail:

Of course, the above figure only analyzes the use process of cipher. In fact, another important step is the key processing, but the key processing is related to the use of specific algorithms, so it is not reflected in the figure. Put another official description of the loading process of cipher:

The main processes include:

Use of cipher

In order to facilitate the use of cipher, it is best to introduce @ h first_ 291_ 419@apache -Codec dependency, which can simplify hex, Base64 and other operations.

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

In most cases, the values of the elements in the encrypted byte array are not within the range of Unicode code points. What you see on the surface is garbled code. In fact, they are meaningful. Therefore, it is necessary to consider converting this byte array into a non garbled string for transmission. Common methods include hex (binary to hexadecimal), Base64, etc. In the following example, the exception types are not handled uniformly. Do not imitate them. In addition, all strings converted into byte arrays do not specify character encoding, so they can only be processed in non Chinese plaintext.

Encryption mode

In encryption mode, cipher can only be used for encryption, which is mainly determined by opmode in init method. for instance:

public String encryptByAes(String content,String password) throws Exception {
	//这里指定了算法为AES_128,工作模式为EBC,填充模式为NoPadding
	Cipher cipher = Cipher.getInstance("AES_128/ECB/NoPadding");
	KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
	//因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
	keyGenerator.init(128,new SecureRandom(password.getBytes()));
	SecretKey secretKey = keyGenerator.generateKey();
	SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),"AES");
	//基于加密模式和密钥初始化Cipher
	cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);
	//单部分加密结束,重置Cipher
	byte[] bytes = cipher.doFinal(content.getBytes());
	//加密后的密文由二进制序列转化为十六进制序列,依赖apache-codec包
	return Hex.encodeHexString(bytes);
}

In fact, the whole process of using cipher is very simple, but the more complex is the key generation process. Note that in the above example, since the filling mode is nopadding, the length of the input message to be encrypted must be a multiple of 16 (128bit).

Decryption mode

The use of decryption mode is roughly the same as that of encryption mode. Just reverse the processing process:

public String decryptByAes(String content,String password) throws Exception {
	//这里要把十六进制的序列转化回二进制的序列,依赖apache-codec包
	byte[] bytes = Hex.decodeHex(content);
	//这里指定了算法为AES_128,工作模式为EBC,填充模式为NoPadding
	Cipher cipher = Cipher.getInstance("AES_128/ECB/NoPadding");
	KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
	//因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
	keyGenerator.init(128,"AES");
	//基于解密模式和密钥初始化Cipher
	cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
	//单部分加密结束,重置Cipher
	byte[] result = cipher.doFinal(bytes);
	return new String(result);
}

Note that in the above example, since the filling mode is nopadding, the length of the input message to be encrypted must be a multiple of 16 (128bit).

Packaging key mode and unpacking key mode

The key packaging and unpacking mode is a pair of inverse operations. Its main function is to encrypt and decrypt the key through the algorithm, so as to improve the difficulty of key leakage.

public enum EncryptUtils {

    /**
     * 单例
     */
    SINGLETON;

    private static final String SECRECT = "passwrod";

    public String wrap(String keyString) throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //初始化密钥生成器,指定密钥长度为128,指定随机源的种子为指定的密钥(这里是"passward")
        keyGenerator.init(128,Cipher.SECRET_KEY);
        return new String(key.getEncoded());
    }

    public static void main(String[] args) throws Exception {
        String wrapKey = EncryptUtils.SINGLETON.wrap("doge");
        System.out.println(wrapKey);
        System.out.println(EncryptUtils.SINGLETON.unwrap(wrapKey));
    }
}

Packet (partial) encryption and packet decryption

When a message to be encrypted is very long, we can consider cutting the message into multiple small segments and encrypting each small segment, which is packet encryption. The process of packet decryption is similar and can be regarded as the reverse process of packet encryption. The following is an example of AES algorithm:

import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

/**
 * @author throwable
 * @version v1.0
 * @description
 * @since 2018/8/15 1:06
 */
public enum Part {

	/**
	 * SINGLETON
	 */
	SINGLETON;

	private static final String PASSWORD = "throwable";

	private Cipher createCipher() throws Exception {
		return Cipher.getInstance("AES");
	}

	public String encrypt(String content) throws Exception {
		Cipher cipher = createCipher();
		KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
		//因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
		keyGenerator.init(128,new SecureRandom(PASSWORD.getBytes()));
		SecretKey secretKey = keyGenerator.generateKey();
		SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),"AES");
		//基于加密模式和密钥初始化Cipher
		cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);
		byte[] raw = content.getBytes();
		StringBuilder builder = new StringBuilder();
		//[0,9]
		byte[] first = cipher.update(raw,10);
		builder.append(Hex.encodeHexString(first));
		//[10,19]
		byte[] second = cipher.update(raw,10,10);
		builder.append(Hex.encodeHexString(second));
		//[20,25]
		byte[] third = cipher.update(raw,20,6);
		builder.append(Hex.encodeHexString(third));
		//多部分加密结束,得到最后一段加密的结果,重置Cipher
		byte[] bytes = cipher.doFinal();
		String last = Hex.encodeHexString(bytes);
		builder.append(last);
		return builder.toString();
	}

	public String decrypt(String content) throws Exception {
		byte[] raw = Hex.decodeHex(content);
		Cipher cipher = createCipher();
		KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
		//因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
		keyGenerator.init(128,"AES");
		//基于解密模式和密钥初始化Cipher
		cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
		StringBuilder builder = new StringBuilder();
		//[0,14]
		byte[] first = cipher.update(raw,15);
		builder.append(new String(first));
		//[15,29]
		byte[] second = cipher.update(raw,15,15);
		builder.append(new String(second));
		//[30,31]
		byte[] third = cipher.update(raw,30,2);
		builder.append(new String(third));
		//多部分解密结束,得到最后一段解密的结果,重置Cipher
		byte[] bytes = cipher.doFinal();
		builder.append(new String(bytes));
		return builder.toString();
	}

	public static void main(String[] args) throws Exception{
		String raw = "abcdefghijklmnopqrstyuwxyz";
		String e = Part.SINGLETON.encrypt(raw);
		System.out.println(e);
		System.out.println(Part.SINGLETON.decrypt(e));
	}
}

The above segment subscript has been given in the note. The segmentation rules are considered by the actual situation. Generally, when the AES encryption and decryption message is small, it can be directly encrypted and decrypted in a single part. This is just for demonstration.

View all providers of cipher in the current JDK

We can directly view all cipher providers and supported encryption and decryption services in the currently used JDK, and simply write a main function:

import java.security.Provider;
import java.security.Security;
import java.util.Set;

public class Main {

	public static void main(String[] args) throws Exception {
		Provider[] providers = Security.getProviders();
		if (null != providers) {
			for (Provider provider : providers) {
				Set<Provider.Service> services = provider.getServices();
				for (Provider.Service service : services) {
					if ("Cipher".equals(service.getType())) {
						System.out.println(String.format("provider:%s,type:%s,algorithm:%s",service.getProvider(),service.getType(),service.getAlgorithm()));
					}
				}
			}
		}
	}
}

The JDK used by the author is the last updated version 8u181 (1.8.0_181) of jdk8. Run the main function and the output is as follows:

provider:SunJCE version 1.8,type:Cipher,algorithm:RSA
provider:SunJCE version 1.8,algorithm:DES
provider:SunJCE version 1.8,algorithm:DESede
provider:SunJCE version 1.8,algorithm:DESedeWrap
provider:SunJCE version 1.8,algorithm:PBEWithMD5AndDES
provider:SunJCE version 1.8,algorithm:PBEWithMD5AndTripleDES
provider:SunJCE version 1.8,algorithm:PBEWithSHA1AndDESede
provider:SunJCE version 1.8,algorithm:PBEWithSHA1AndRC2_40
provider:SunJCE version 1.8,algorithm:PBEWithSHA1AndRC2_128
.....输出内容太多忽略剩余部分

extend

Because the transformation supported by Java Native is limited, sometimes we need to use some algorithms, and other working modes or filling modes cannot be supported. At this time, we need to introduce a third-party provider or even implement the provider ourselves. The common third-party provider is bouncy castle (BC). At present, the latest dependencies of BC are:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

For example, Java Native does not support the aeswrap algorithm, so you can introduce the dependency of BC and then use the conversion mode aeswrap.

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.Security;

public enum EncryptUtils {

	/**
	 * SINGLETON
	 */
	SINGLETON;

	private static final String SECRET = "throwable";
	private static final String CHARSET = "UTF-8";

    //装载BC提供商
	static {
		Security.addProvider(new BouncyCastleProvider());
	}


	private Cipher createAesCipher() throws Exception {
		return Cipher.getInstance("AESWRAP");
	}

	public String encryptByAes(String raw) throws Exception {
		Cipher aesCipher = createAesCipher();
		KeyGenerator keyGenerator = KeyGenerator.getInstance("AESWRAP");
		keyGenerator.init(128,new SecureRandom(SECRET.getBytes(CHARSET)));
		SecretKey secretKey = keyGenerator.generateKey();
		SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),"AESWRAP");
		aesCipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);
		byte[] bytes = aesCipher.doFinal(raw.getBytes(CHARSET));
		return Hex.encodeHexString(bytes);
	}

	public String decryptByAes(String raw) throws Exception {
		byte[] bytes = Hex.decodeHex(raw);
		Cipher aesCipher = createAesCipher();
		KeyGenerator keyGenerator = KeyGenerator.getInstance("AESWRAP");
		keyGenerator.init(128,"AESWRAP");
		aesCipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
		return new String(aesCipher.doFinal(bytes),CHARSET);
	}

	public static void main(String[] args) throws Exception {
		String raw = "throwable-a-doge";
		String en = EncryptUtils.SINGLETON.encryptByAes(raw);
		System.out.println(en);
		String de = EncryptUtils.SINGLETON.decryptByAes(en);
		System.out.println(de);
	}
}

It should be noted in the above example that because aeswrap algorithm is used, the length of the input message to be encrypted must be a multiple of 8.

Summary

After mastering the usage of cipher and some knowledge of transformation mode transformation, the main factor affecting us to write encryption and decryption module code is the principle or use of encryption and decryption algorithm, which requires us to learn special knowledge related to encryption and decryption algorithm. In addition, sometimes we find that different platforms or languages cannot decrypt and encrypt each other using the same encryption algorithm. In fact, the reason is very simple. Most of the reasons are caused by different selection of working mode or filling mode. Excluding these two points, the remaining possibility is that the implementation of the algorithm is different, Judge and find solutions based on these three factors (or the only factor of transformation). For the principle and working mode of encryption and decryption algorithm, please refer to the following materials.

reference material:

In addition, some special methods, such as @ H_ 291_ 419@Ciper #Updateadd hasn't encountered any usage scenarios yet, so we won't write a demo that hasn't been practiced here. The next article will introduce the basic principles of some mainstream encryption and decryption algorithms and their applications through cipher.

(end of this article c-7-d)

The official account of Technology (Throwable Digest), which is not regularly pushed to the original technical article (never copied or copied):

Entertainment official account ("sand sculpture"), select interesting sand sculptures, videos and videos, push them to relieve life and work stress.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>