This article is the 18th day article of Java Advent Calendar 2016. The day before, it was fullmetal248's "A story about trying hard to decompile JAR files". Tomorrow is yumix_h's "birthday frame!"
Encryption is the process of converting the original data (such as a character string) into something that you don't understand at first glance. You can use the decryption key to restore the original string, which is called decryption. The Java language has a cryptographic processing library in the javax.crypto package, so cryptographic processing can be performed using various encryption methods.
The following describes AES encryption, which is often used in encryption processing. Also, since I created a library class for using AES encryption in a program, I will explain the source code and how to use it.
Let's start with a basic knowledge of AES cryptography.
In the late 1990s, it became clear that the DES ciphers that the US government had used for public purposes would be broken by some personal computers (perhaps using about 30). Therefore, we decided to hold a world competition and solicit new cryptosystems. There were entries such as elliptic curve cryptography from Japan, but AES cryptography was finally adopted.
AES ciphers are cut into small blocks and encrypted little by little. At that time, the CBC mode is to take the XOR of the block that was encrypted immediately before and the block that is to be encrypted next, and then encrypt it. Since the encryption of the previous block affects the encryption of the next block, it has the excellent feature that it is not possible to guess what kind of encryption a specific character string will be encoded by comparing the plaintext and the encryption. There is.
IV(Initial Vector)
In CBC mode, the first block has no XOR overlapping data blocks. Therefore, for the first block, we artificially create a value, but this is IV. IV is a 128-bit string when using standard Java classes.
If you give a different IV each time during encryption processing, you can make different encryption each time even if the plaintext is the same. So IV uses a random string. Use RandomStringUtils in Commons Library (commons-lang3-3.4-bin.zip) to get random strings , You can easily generate by specifying the length and the type of character to use.
A key string used for both encryption and decryption. This should also be 128 bits, or exactly 16 characters. You can freely create the content, but if the decryption key is too long, it will be truncated, and if it is too short, the character string will be supplemented.
The following is a library class that encrypts and decrypts. To do the encryption, just create an instance of the Crypt class and use the encrypto or decrypto method. However, since it is necessary to save the key and IV when using it, a sample of specific usage will be posted later.
First, we need a commons class, so here's a pom.xml file for it. I wrote the meaning and usage of pom in Posted on 13th, so please have a look there.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>crypto</groupId>
<artifactId>crypto</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Next is the Crypto class for cryptographic processing. The project is created in Eclipse after creating a normal Java project, right-clicking the project and selecting [Configure] ⇒ [Convert to maven project].
package com.tkxwebs.crypto;
import java.io.Serializable;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
*AES cipher/Cryptographic processing class in CBC mode
* @author Takashi Kawaba [email protected]
*/
public class Crypto implements Serializable {
private Cipher encrypter; //Cryptographic device for encryption
private Cipher decrypter; //Cryptographic device for decryption
/*Constructor (arguments are decryption key and IV)
*Both arguments are from String to byte[]Specify what was converted to
*/
public Crypto(byte[] secretKey, byte[] ivs) throws Exception{
IvParameterSpec iv = new IvParameterSpec(ivs); //Create initial value for start block at encryption
SecretKeySpec key = new SecretKeySpec(secretKey, "AES"); //Create a set of encryption method + decryption key
encrypter = Cipher.getInstance("AES/CBC/PKCS5Padding"); //Create an encryption device by specifying the encryption method and generation method
encrypter.init(Cipher.ENCRYPT_MODE, key, iv); //Set the cipher to encryption mode
decrypter = Cipher.getInstance("AES/CBC/PKCS5Padding"); //Create another cipher
decrypter.init(Cipher.DECRYPT_MODE, key, iv); //Set the cipher to decryption mode
}
/*Method to perform encryption process*/
public String encrypto(String text) throws Exception {
byte[] crypto = encrypter.doFinal(text.getBytes()); //Encrypt
byte[] str64 = Base64.getEncoder().encode(crypto); //Convert to an array of characters for display
return new String(str64); //Make it a character string
}
/*Method to perform decryption process*/
public String decrypto(String str64) throws Exception {
byte[] str = Base64.getDecoder().decode(str64); //Revert the cipher string to the original binary
byte[] text = decrypter.doFinal(str); //Decrypt
return new String(text); //Convert to a string and return
}
}
The following FileUtil class is a utility used to store the encryption key and IV.
package com.tkxwebs.crypto;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class FileUtil {
public static void writeBytes(byte[] b, String path) {
try(ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(path))) {
o.write(b);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] readBytes(String path) {
byte[] b = new byte[16];
try(ObjectInputStream in = new ObjectInputStream(new FileInputStream(path))) {
in.read(b, 0, 16);
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
}
Let's see how to actually use it.
When do you use cryptography? It encrypts passwords, etc. Then, it is decrypted when collating, for example, in the login process. Obviously, encryption and decryption are performed at different times.
Therefore, the decryption key and IV must be recorded (and further encrypted) in a file or database in the system for storage. Here, the further encryption process is omitted, but in any case, the decryption key and the decryption key must be written out or read out at the timing of encryption and decryption.
In this usage example, a utility (FileUtil class) that saves the byte array as an object in a file is used for reading and writing.
package sample;
import org.apache.commons.lang3.RandomStringUtils;
import com.tkxwebs.crypto.Crypto;
import com.tkxwebs.crypto.FileUtil;
public class CryptoExample {
public static void main(String args[]) {
/*******************************************************************************
*Since encryption and decryption are usually done separately, the decryption key and IV are files.
*Save it in and load it at runtime to use it
*******************************************************************************/
/*------------------------------------------------------------------------------
Data preparation
--------------------------------------------------------------------------------*/
byte[] iv = RandomStringUtils.randomAlphanumeric(16).getBytes();// IV(Initial value 128-bit fixed length for start block at the time of encryption)
byte[] key = "kawaba_2015_key_".getBytes(); //Decryption key(128-bit fixed length)
/*------------------------------------------------------------------------------
Save the IV and decryption key in a file
--------------------------------------------------------------------------------*/
FileUtil.writeBytes(iv, "iv");
FileUtil.writeBytes(key, "secret");
/*------------------------------------------------------------------------------
Data display
--------------------------------------------------------------------------------*/
System.out.println("IV(Initial value for start block)="+new String(iv)+ "("+iv.length + "byte)");
System.out.println("Cryptanalysis key =" + new String(key) + "(16byte)");
/*------------------------------------------------------------------------------
Encryption process
--------------------------------------------------------------------------------*/
String source = "What should i do"; //String to encrypt
String result = ""; //Encrypted result string
try { //Create a Crypto object
Crypto c = new Crypto(FileUtil.readBytes("secret"), FileUtil.readBytes("iv")); //Read decryption key and IV from file
result = c.encrypto(source); //Get an encrypted string
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Cryptography =" + result); //Display the cipher string
/*------------------------------------------------------------------------------
Decryption process
--------------------------------------------------------------------------------*/
try { //Create a Crypto object
Crypto c2 =new Crypto(FileUtil.readBytes("secret"), FileUtil.readBytes("iv")); //Read decryption key and IV from file
result = c2.decrypto(result); //Get the decrypted string
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Decryption =" + result); //Display the original string
}
}
When I run this code, I get the following: Try it a few times and make sure that the same string is converted to a different cipher each time.
IV(Initial value for start block)=u7aTAfOMMKJvD02S(16byte)
Decryption key = kawaba_2015_key_(16byte)
Cryptography = piiKP/GX3HcykHbXAHjptPDtEORdx6oASa2luFtAbZA=
Decryption = What if