Java cryptography fundamentals
The development history
- Classical cryptography such as Caesar cipher, drum cipher
- Modern cryptography, such as the German Enigma machine, was cracked by Turing
- Modern cryptography
Coding algorithm
Not encryption and decryption, created to facilitate the transfer of data between networks/local storage byte arrays
Base64
Base64 is one of the most common encoding methods for transmitting 8Bit bytecode on the network. Base64 is a method to represent binary data based on 64 printable characters. For details about MIME, see RFC2045 to RFC2049.
Base64 encoding is a process from binary to character that can be used to pass longer identity information in HTTP environments. Base64 encoding is unreadable and can be read only after decoding.
Base64 is widely used in various fields of computer because of the above advantages, but because the output content includes more than two “symbol class” characters (+, /, =), different application scenarios are separately developed Base64 “varieties”. To unify and normalize the output of Base64, Base62x is regarded as an improved version of unsymbolized Base64.
Write an example to implement this:
/ * * *@author cheung0
*/
public class Base64Test {
// Define the encoding type as UTF-8
private static final String UTF8 = StandardCharsets.UTF_8.name();
public static void main(String[] args) throws UnsupportedEncodingException {
/** * use the JDK to implement */
String str = "I love Java!";
/ / code
String encodeStr1 = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
System.out.println("Coding result:" + encodeStr1);
/ / decoding
byte[] decodeStr1 = Base64.getDecoder().decode(encodeStr1.getBytes(StandardCharsets.UTF_8));
System.out.println("Decoding result:" + new String(decodeStr1,UTF8));
/** * Use third-party SDK */
/ / code
String encodeStr2 = org.apache.commons.codec.binary.Base64.encodeBase64String(str.getBytes(StandardCharsets.UTF_8));
System.out.println("Coding result:" + encodeStr2);
/ / decoding
byte[] decodeStr2 = org.apache.commons.codec.binary.Base64.decodeBase64(encodeStr1.getBytes(StandardCharsets.UTF_8));
System.out.println("Decoding result:" + newString(decodeStr2,UTF8)); }}Copy the code
Print result:
SSBsb3ZlIEphdmEh I love Java! SSBsb3ZlIEphdmEh I love Java! Process terminated, exit code 0Copy the code
Base64 encoding is in groups of three bytes, populated with =
URL encoding
When we surf the web, there are some cases where urls with Chinese characters are encoded into a bunch of %’s and numbers, which is actually URL encoding
When the front end sends a GET request, the request format is Application/X-www-form-urlencoded, which is actually URLcode, and the back end will process it accordingly
Write an example:
/ * * *@author cheung0
*/
public class URLEncodeTest {
// Define the encoding type as UTF-8
private static final String UTF8 = StandardCharsets.UTF_8.name();
public static void main(String[] args) throws UnsupportedEncodingException {
/** * URL encoding */
String str = "I love writing Java";
/ / code
String encodeStr = URLEncoder.encode(str,UTF8);
System.out.println("Coding result:" + encodeStr);
/ / decoding
String decodeStr = URLDecoder.decode(encodeStr,UTF8);
System.out.println("Decoding result:"+ decodeStr); }}Copy the code
Print result:
Encoding result: %E6%88%91%E7%88%B1%E5%86%99Java decoding result: I love to write Java process has ended, exit code 0Copy the code
The algorithm
define
The main feature of message summarization algorithm is that the encryption process does not need a key, and the encrypted data cannot be decrypted. Only THE CRC32 algorithm can be decrypted backwards, and the same ciphertext can be obtained only when the same plaintext data is input through the same message summarization algorithm.
Digest algorithm is also called Hash algorithm, Hash function, number digest, message digest. It is a one-way algorithm. Users can use the hash algorithm to generate a unique hash value of a specific length for the target information, but cannot use the hash value to retrieve the target information
Application scenarios
Password, information integrity check, digital signature
Common algorithms
- Message-digest Algorithm (MD5) result contains 128 bits (16 bytes)
- Secure Hash Algorithm (SHA) Secure Hash Algorithm
- sha-256
- sha-0,sha-1,sha-512
- Message Authentication Code (MAC) is a hash function with a key
- MD2 MD4 HAVAL
Java implementation
MD5:
First use the native JDK implementation
/ * * *@author cheung0
*/
public class MD5Test {
// Define the encoding type as UTF-8
private static final String UTF8 = StandardCharsets.UTF_8.name();
public static void main(String[] args) throws Exception {
/**
* JDK原生实现
*/
String str = "I love the Java";
String algorithm = "MD5";
// Get the message digest algorithm object
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// Get the byte array of the original content
byte[] originBytes = str.getBytes(UTF8);
// Get the summary result
byte[] digestBytes = messageDigest.digest(originBytes);
// Convert each byte to a hexadecimal character, and finally concatenate the hexadecimal character
String hexStr = convertBytestoHexStr(digestBytes);
System.out.println("Hex string:" + hexStr);
}
/** * Convert a byte array to the complement 0 of a hexadecimal string@param digestBytes
* @return* /
private static String convertBytestoHexStr(byte[] digestBytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b:digestBytes) {
// Ensure the consistency of the complement binary storage is set to the last eight bits
String hex = Integer.toHexString(b&0xff);
// If the hexadecimal number is a single digit, 0 is added before it
if (hex.length() == 1) {
hex = "0" + hex;
}
stringBuilder.append(hex);
}
returnstringBuilder.toString(); }}Copy the code
Print result:
Hex string: b2f973e181bc4dedaded7887c85a0a23 process has finished, exit code 0Copy the code
Since the digest results in a byte array and cannot be printed directly (garbled), I wrote a function by hand to convert the byte stream into a hexadecimal string.
A note: We declare a StringBuilder tool for concatenating strings. Since THE MD5 and digest results in a byte stream of 128 characters, or 16 bytes, I just iterate over each byte and convert it to a hexadecimal number. And because in a Java virtual machine, run at compile time each byte will be promoted to int type of data, each byte data to 4 bytes (this is the design of the Java virtual machine), and because there are some bytes is negative, negative number in the computer in the form of complement, when it was promoted to data type int, high fill 1. For example, -127, the eight bits represent 1000 0001, and the highest bit is the sign bit. When it is converted to an int, the high value is filled with 1, and the result is: 1111 1111 1111 1111 1111 1111 1000 0001, so that the truth value is kept unchanged. In this case, we only need to convert the last byte to the corresponding hexadecimal number, so with 0xFF(1111 1111), the last eight bits are reserved. Let’s say we have -127, 1111 1111 1111 1111 1111 1000 0001&0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 1000 0001 is a hexadecimal number 81. That is, when we convert it to a hexadecimal string, we do not care about its numerical correctness, but only the correctness of its binary stream form on the computer, so we only cut the last eight bits and convert it.
Of course, we can also be lazy and not write our own conversion methods, because there are encapsulated utility classes:
String hexStr = DigestUtils.md5Hex(digestBytes);
Copy the code
You can still get the same result:
Hex string: b2f973e181bc4dedaded7887c85a0a23 process has finished, exit code 0Copy the code
Sha-256,SHA-512 and other algorithms are the same as MD5, but with more encrypted bytes, the implementation details are the same. If you are interested, you can change the algorithm to the corresponding algorithm.
MAC:
On the MAC, MD5,SHA-256 and other algorithms are added with a key value, which is often called “salted “**, to make it more secure.
Greatly reduced hackers through enumeration or rainbow tables and other means to break!
Here’s an example:
public class MacTest {
public static void main(String[] args) {
String str = "I love the Java";
String key = "666";
// Use the codec tool to obtain MAC results under various algorithms
String hmacMD5HexStr = new HmacUtils(HmacAlgorithms.HMAC_MD5,key.getBytes(StandardCharsets.UTF_8)).hmacHex(str.getBytes(StandardCharsets.UTF_8));
String hmacSHA256HexStr = newHmacUtils(HmacAlgorithms.HMAC_SHA_256,key.getBytes(StandardCharsets.UTF_8)).hmacHex(str.getBytes(StandardCharsets.UTF_8) ); String hmacSHA512HexStr =newHmacUtils(HmacAlgorithms.HMAC_SHA_512,key.getBytes(StandardCharsets.UTF_8)).hmacHex(str.getBytes(StandardCharsets.UTF_8) ); System.out.println("hmacMD5HexStr: " + hmacMD5HexStr);
System.out.println("hmacSHA256HexStr: " + hmacSHA256HexStr);
System.out.println("hmacSHA512HexStr: "+ hmacSHA512HexStr); }}Copy the code
Print result:
hmacMD5HexStr: 9baa3a835a69cb9382f9374625de1876 hmacSHA256HexStr: d3c89c5945411a54a4fe96deaf0e0c2c22f61b3d43c98daae3b8b2f911d15003 hmacSHA512HexStr: ce37dd5da4c6ab796a6e2485cb35b2d3824cfaf0c99f72421aa5580caa96d4e9e5b91c46881eeaddb0f4ea65012dab753edab13e75be2190ea97d7f5 The EEB67c96 process has ended. Exit code 0Copy the code
Symmetric encryption
Baidu Baike Definition
An encryption algorithm that requires the same key for encryption and decryption. Because of its speed, symmetric encryption is often used when the sender of a message needs to encrypt a large amount of data. Symmetric encryption is also known as key encryption.
Symmetry means that the two parties using this encryption method use the same key for encryption and decryption. Keys are instructions that control the encryption and decryption process. An algorithm is a set of rules that dictate how encryption and decryption are performed.
Therefore, the security of encryption not only depends on the encryption algorithm itself, but also the security of key management. Since both encryption and decryption use the same key, how to safely transfer the key to the decryptor has become a problem that must be solved
Common algorithms
- Data Encryption Standard (DES) is outdated
- Advanced Encryption Standard (AES) replaces DES
- 3DES Blowfish IDEA RC4 RC5 RC6……
classification
- Block encryption (block encryption)
- The sequence encryption
Java program to implement AES:
public class AESTest {
public static void main(String[] args) throws Exception{
/ / the original
String message = "I love the Java";
System.out.println("Original:" + message);
// Define a 128-bit key
byte[] key = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);
/ / encryption
byte[] data = message.getBytes(StandardCharsets.UTF_8);
/ / encryption
byte[] encrypted = encrypt(key,data);
System.out.println("Encryption result:" + Base64.getEncoder().encodeToString(encrypted));
/ / decryption
byte[] decrypted = decrypt(key, encrypted);
System.out.println("Decryption result:" + new String(decrypted, StandardCharsets.UTF_8));
}
/ / encryption:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
/ / decryption:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
returncipher.doFinal(input); }}Copy the code
Print result:
I love the Java process has ended, exit code 0Copy the code
Asymmetric encryption
Baidu Baike Definition
Symmetric encryption algorithm uses the same secret key for encryption and decryption. Asymmetric encryption algorithms require two keys, a public key and a private key, for encryption and decryption.
Encryption and decryption use two different keys. The public key can be given to anyone, and the private key is always kept for itself
A reason for
Symmetric encryption uses the same secret key, but different secret keys are used for different original contents, resulting in a large number of secret keys and difficult to maintain
For example, with symmetric encryption, you need to produce N*(n-1)/2 secret keys, and then each person needs to manage n-1 secret keys, which is difficult to manage. With asymmetric encryption, when N people communicate with each other, only N key pairs need to be produced, and each person only needs to manage his or her own key pair!
RSA algorithm
Asymmetric encryption, the most typical is the RSA algorithm. RSA was invented by three brothers, Ron Rivest, Adi Shamir and Leonard Adleman, hence the name RSA algorithm. The RSA algorithm generates a key pair: a public key and a private key. Through the public key encryption content, only the private key can unlock, as for why, design to the number theory cryptography related mathematical knowledge, temporarily do not delve into. Therefore, in the communication between the two parties, one party should first ask for the other party’s public key, use the other party’s public key to encrypt their own sensitive content to be sent to the other party, the other party after receiving the decryption with their private key, as long as the private key is not stolen, the third party can not crack the sensitive content!
Use the Java standard library to simulate:
public class RSATest {
public static void main(String[] args) throws Exception {
// Sensitive content to send
byte[] context = "I lOVE YOU!".getBytes(StandardCharsets.UTF_8);
// Create a key pair
Person tony = new Person("Tony");
// Encrypt with Tony's public key:
byte[] pk = tony.getPublicKey();
System.out.println(String.format("Key: %x".new BigInteger(1, pk)));
byte[] encrypted = tony.encrypt(context);
System.out.println(String.format("Public key encryption result: %x".new BigInteger(1, encrypted)));
// Decrypt with Tony's private key:
byte[] sk = tony.getPrivateKey();
System.out.println(String.format("Private key: %x".new BigInteger(1, sk)));
byte[] decrypted = tony.decrypt(encrypted);
System.out.println(newString(decrypted, StandardCharsets.UTF_8)); }}class Person {
String name;
/ / the private key
PrivateKey sk;
/ / the public key
PublicKey pk;
// There are parameters
public Person(String name) throws NoSuchAlgorithmException {
this.name = name;
// Generate a key pair
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
kpGen.initialize(1024);
KeyPair kp = kpGen.generateKeyPair();
this.sk = kp.getPrivate();
this.pk = kp.getPublic();
}
// Export the private key as bytes
public byte[] getPrivateKey() {
return this.sk.getEncoded();
}
// Export the public key to bytes
public byte[] getPublicKey() {
return this.pk.getEncoded();
}
// Encrypt with public key:
public byte[] encrypt(byte[] message) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, this.pk);
return cipher.doFinal(message);
}
// Decrypt with private key
public byte[] decrypt(byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, this.sk);
returncipher.doFinal(input); }}Copy the code
Print result:
Public key: 30819f300d06092a864886f70d010101050003818d0030818902818100ca2c25ebe9d687856dde5e82e10f5e7242f878f4fb18996736743b9772047a 431cb0e2ec07a2cca7f94a4e9a38d375d95499326334a2239810ad6989c75596823cf15cf03dd14eafe6dec987f422729f866af13e0df4334e4eacf3 15 a8549cf12b3a3515971ce6999d32b4f7953534b8538091b91adcc420dc30e450156b7c650203010001 public-key encryption results: 9572d508d34bcabac85df021f263be67a9a3140079222e377986761542b95d5a05da179bbe8e3324f3916c52e63435366931f12d3ce085ae7acf591d bb20253b94a5c671c32234570a3e8d7f0332b81e66c80d69dd4e1260e1362e3d377505a95d751b8716f85a7b3b01f3819742141c1ac93e65d085a1ec 6792 dad062b174cb private key: 30820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100ca2c25ebe9d687856dde5e82e10f5e7242f878f4fb1899 6736743b9772047a431cb0e2ec07a2cca7f94a4e9a38d375d95499326334a2239810ad6989c75596823cf15cf03dd14eafe6dec987f422729f866af1 3e0df4334e4eacf315a8549cf12b3a3515971ce6999d32b4f7953534b8538091b91adcc420dc30e450156b7c6502030100010281805fd0d4981e5702 1b869aa1083e49de6520c049f3311dd3764b2483299f6be7d5eebf168cee8185a5064ce53bca3acddb967094a4d7c9103d7d89f23ece2e0e0a0b53a3 bbb71919a1fcb0fa1098f7366f17746e2b63c35fad1437479472c0ae1df435afe7b9df238563dabd512351af0d448d498205dedac5d1cfb7a139cf8b f1024100f40c77f7d6108f2dec4266509590c9c172beabfe118541f1ced82fc9c00877a6afff9f9d338246fdecff85488808e9731a3cb9df6acaad06 395158d5226f85cf024100d412b201a565aee62f127da4dd99151debd1699d99bc6c5ce556874361e0332621864fbaf87fb5b1521b5f9684874ce4fd f778a4a58be742e2c94fee756f1b8b02407c8c18758cf3aa7e7f426bc0d873a9e365d1d528b67c51693c6cac06c4500df02d85c14992cdfbb8ff4870 16d205ea4de9a7f01c0afe204b3ad93f0296ae5f9502400eb8617cb5c352198e28e569bd2bf40848a71782a5fa2b37637fd711b9487ba468ed4eb976 a83eaf5938a730e67011c94f4b8f27368a7879ef0df42b64215b3302406bfbedbea8e7b86c36c42c3455a1cab7ffbcd90fef3ed2de6f5257d073eed8 d4d6faedd88806b4434160b0f635c0ab9e0c5c3d5693fc616fc4943feec10507bd I lOVE YOU! Process terminated, exit code 0Copy the code
As you can see, the private key is much longer than the public key! Take the RSA algorithm as an example. Its keys have different lengths, such as 256/512/1024/2048/4096. The longer the password, the stronger it is and, of course, the slower the calculation.
Therefore, asymmetric encryption and symmetric encryption are used together in practical applications to effectively solve the problem of slow asymmetric encryption. For example, teacher Liao Xuefeng gave the following example:
Xiao Ming needs to transfer an encrypted file to Xiao Hong. They first exchange their public keys and then:
- Xiao Ming generates a random AES password, and then encrypts the password through RSA with Xiao Hong’s public key and sends it to Xiao Hong.
- Xiao Hong decrypts the AES password with her own RSA private key.
- Both parties use this shared AES password to encrypt their communication.
It can be seen that asymmetric encryption actually applies in the first step, encrypting the “AES password”. That is, the browser and the server exchange AES passwords through RSA first, and then use AES symmetric encryption instead of RSA asymmetric encryption.
Signature algorithm
It can be concluded from the above that when one party sends a message to the other party, it uses the other party’s public key to encrypt the message, and the other party uses the private key to decrypt the message when it receives it. So what’s the point of encrypting something with a private key that anyone with a public key can easily access?
The meaning is the signature! ** What is a signature? In ancient times, when emperors issued imperial edicts, they would stamp their official seals, or when they wrote letters to others, they also signed their names. These are actually signatures! Thus, the meaning of signature is to show identity, this edict is written by the emperor, all must not be violated! And in the signature algorithm, there is a very important role, is to verify the integrity of the data, in case the edict was intercepted by a third party, tamper with the content!
Therefore, the ciphertext encrypted by the private key is actually a digital signature. To verify the signature, only the public key of the private key owner can be used for decryption verification. The purpose of using a digital signature is to confirm that a message was indeed sent by a sender. It is impossible for anyone to forge a message, and the sender cannot deny it.
In practice, the signature is not actually for the original message, but for the hash of the original message. Verifying a signature is actually decryption with a public key.
The decrypted hash is then compared to the hash of the original message.
Since users always sign with their private key, the private key is equivalent to the user’s identity. Public keys are used to authenticate users externally.
Common digital signature algorithms are as follows:
- MD5withRSA
- SHA1withRSA
- SHA256withRSA
Write a Java program to simulate the signature algorithm:
public class SHA1withRSA {
public static void main(String[] args) throws Exception {
// Generate RSA public/private key:
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
kpGen.initialize(1024);
KeyPair kp = kpGen.generateKeyPair();
PrivateKey sk = kp.getPrivate();
PublicKey pk = kp.getPublic();
// Message to be signed:
byte[] message = "I LOVE YOU!".getBytes(StandardCharsets.UTF_8);
// Sign with private key:
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(sk);
s.update(message);
byte[] signed = s.sign();
System.out.println(String.format("signature: %x".new BigInteger(1, signed)));
// Verify with public key:
Signature v = Signature.getInstance("SHA1withRSA");
v.initVerify(pk);
v.update(message);
boolean valid = v.verify(signed);
System.out.println("valid? "+ valid); }}Copy the code
Print result:
signature: bf68e25ab72a2fd7971065c460d6b6d131533c299e527111d88f7cf1a35968f109cc863b5a23c2e8f50ed6f80bbbe95a42eec14ddd7770e3ace69892 30cf5d2bd3a03736fcd0f5cfd6a4a5988fe02b48c3306165f887803fe0845a964fce3210d04ef77afb42b5a47756731b1bbff5264ec4226ef8fa0db3 16ad74a37e7a94c6 valid? True The process has ended, exit code 0Copy the code
Let me draw a picture to clarify:
When sending messages, Tony uses AES to encrypt the content, RSA to encrypt the AES password, and private key to encrypt the digest generated by the digest algorithm to generate a digital signature. Sunsan in receiving, first use the private key decryption to obtain AES password, and then use AES to obtain the original content is “I LOVE YOU!” At the same time, decrypt the digital signature with the public key and get the abstract content. Sunsan compares the obtained abstract with the one generated from the original text. If it is consistent, it indicates that this confession is indeed from Tony, and the content is true and has not been modified!
Asymmetric encryption and symmetric encryption and signature algorithm cooperation, to ensure the “privacy” of message content, but also confirm the “identity” of the sender and the “integrity” of the content
The digital certificate
Digital certificate is a security standard that combines various cryptography algorithms to realize data encryption and decryption, identity authentication, signature and other functions.
Digital certificates protect against man-in-the-middle attacks because they use chain signature authentication, which means that the Root CA is used to sign the certificate at the next level, and so on, until the final user certificate. The Root CA certificate is embedded in the operating system. Therefore, any DIGITAL certificate authenticated by the CA can be verified to ensure that the certificate itself is not forged.
The HTTPS protocol that we often use on the Internet is the application of digital certificates. The browser automatically verifies the validity of the certificate:
A digital certificate stores a public key. The corresponding private key must be carefully stored. If leaked, it will cause a catastrophic security accident.