Cabbage Java self study room covers core knowledge
Accidentally, I hear from fellow programmers that base64 and MD5 are both used for encryption. They have no concept of encryption, and they think compression is encryption. So today I come here to explain what encryption is, the principle and use of encryption.
1. Encryption concept
Cryptography is the technical science of creating and breaking codes. Based on mathematics, it alternates between encryption and decryption, attack and defense, and spear and shield confrontations. From the point of view of mathematical algorithm, it includes symmetric cryptography algorithm, asymmetric cryptography algorithm and hash algorithm.
Let’s take a look at some concepts often mentioned in encryption.
- Plaintext: Plaintext refers to raw data that has not been encrypted.
- Ciphertext: After the plaintext is encrypted by an encryption algorithm, it becomes ciphertext to ensure the security of the original data. Ciphertext can also be decrypted to get the original plain text.
- Key: A key is a parameter that is entered in the algorithm that converts plaintext to ciphertext or ciphertext to plaintext. Keys are classified into symmetric keys and asymmetric keys, which are used for symmetric encryption and asymmetric encryption respectively.
2. Symmetric encryption
Symmetric encryption is also called private key encryption, that is, the sender and receiver of information use the same key to encrypt and decrypt data. Symmetric encryption is characterized by fast algorithm disclosure, encryption and decryption, and is suitable for encrypting large amounts of data.
The encryption process is as follows: Plaintext + encryption algorithm + private key => ciphertext
The decryption process is as follows: Ciphertext + decryption algorithm + private key => plaintext
The key used in symmetric encryption is called a private key. A private key is a private key that cannot be leaked. The private key used in encryption and decryption is the same key, which is the reason why encryption is called “symmetric”. Because the algorithm of symmetric encryption is public, once the private key is leaked, the ciphertext is easy to be cracked. Therefore, the disadvantage of symmetric encryption is the difficulty of key security management.
If you don’t quite understand, look at this simple example:
A said to B, I have a lock, we will send messages to each other in the future, put the message in the box, and then use this lock to lock and pass, this lock has two identical keys, we each a, finish saying a handed the key to B.
3. Asymmetric encryption
Asymmetric encryption is also called public key encryption. Asymmetric encryption is more secure than symmetric encryption. Symmetrically encrypted communication uses the same key, and if one party’s key is compromised, the whole communication can be broken. Asymmetric encryption, on the other hand, uses a pair of keys, a public key and a private key, that come in pairs. The private key is kept by itself and cannot be disclosed. A public key is a public key that can be obtained by anyone. Encrypt with either the public or private key and decrypt with the other.
The ciphertext encrypted by the public key can only be decrypted by the private key. The process is as follows:
Plaintext + encryption algorithm + Public key => ciphertext; Ciphertext + decryption algorithm + private key => plaintext
Asymmetric encryption is “asymmetric” because encryption and decryption use two different keys. The disadvantage of asymmetric encryption is that it takes a long time to encrypt and decrypt, and the speed is slow. It is only suitable for encrypting a small amount of data.
If you don’t quite understand, look at this simple example:
A said to B, I have A lock of model A, corresponding to key A. I will give you A large box of LOCK A, but key A will not give you. In the future, if you send me messages, you can use A lock to lock it in the box for me, and THEN I can open it by myself with key A.
B said to A, I have type B locks corresponding to key B. I will give you a large box of LOCK B, but key B will not be given to you. In the future, if you send me messages, you can use the lock B to lock it in the box to me, and THEN I can use the key B to open it.
4. Comparison of common encryption algorithms
Encryption algorithms can be divided into symmetric encryption and asymmetric encryption. The encryption key of symmetric encryption algorithm is the same as that of decryption key, while the encryption key of asymmetric encryption algorithm is different from that of decryption key. In addition, there is a kind of hash algorithm that does not need the key.
Common symmetric encryption algorithms include DES, 3DES, and AES, common asymmetric algorithms include RSA and DSA, and hash algorithms include SHA-1 and MD5.
4.1. Comparison of hash algorithms
The name of the | security | speed |
---|---|---|
MD5 | In the | fast |
SHA-1 | high | slow |
4.2. Comparison of symmetric encryption algorithms
The name of the | The key name | The running speed | security | Resource consumption |
---|---|---|---|---|
DES | 56 | faster | low | In the |
3DES | 112 bits or 168 bits | slow | In the | high |
AES | 128, 192, 256 bits | fast | high | low |
4.3. Comparison of asymmetric encryption algorithms
The name of the | maturity | The running speed | security | Resource consumption |
---|---|---|---|---|
RSA | high | In the | high | In the |
ECC | high | slow | high | high |
Symmetric encryption key management is difficult, not suitable for the Internet, generally used in the internal system, security is only medium, but the encryption speed is several orders of magnitude faster (software encryption and decryption speed is at least 100 times faster, can encrypt and decrypt M bits of data per second), suitable for large data encryption and decryption processing. Asymmetric encryption is easy to manage and secure, but the encryption speed is slow. It is suitable for encrypting and decrypting small amounts of data or signing data.
5. Use common encryption algorithms
5.1. The MD5 algorithm
MD5 uses hash functions and is typically used to produce a summary of a piece of information to prevent tampering. Strictly speaking, MD5 is not an encryption algorithm but a digest algorithm. MD5 prints a string of 128bits (usually 32 characters in hexadecimal notation), regardless of the length of the input.
Java use cases:
public static final byte[] computeMD5(byte[] content) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); return md5.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }}Copy the code
5.2. SHA1 algorithm
SHA1 is a message digest algorithm as popular as MD5, but SHA1 is more secure than MD5. For messages less than 2 ^ 64 bits in length, SHA1 produces a 160-bit message digest. MD5, SHA1-based information digest features and irreversibility (generally speaking) can be used to check file integrity and digital signatures.
Java use cases:
public static byte[] computeSHA1(byte[] content) { try { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); return sha1.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }}Copy the code
5.3. HMAC algorithm
MAC is a Hash-based Message Authentication Code. HMAC uses the Hash algorithm (such as MD5 and SHA1) to input a key and a Message. Generate a message digest as output. The HMAC sender and receiver both have a key for calculation, but a third party without this key cannot calculate the correct hash value, thus preventing data tampering.
Java use cases:
package net.pocrd.util; import net.pocrd.annotation.NotThreadSafe; import net.pocrd.define.ConstField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; @NotThreadSafe public class HMacHelper { private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class); private Mac mac; / * * * * HmacMD5 / MAC algorithm can choose the following various algorithm HmacSHA1 / HmacSHA256 / HmacSHA384 / HmacSHA512 * / private static final String KEY_MAC = "HmacMD5"; public HMacHelper(String key) { try { SecretKey secretKey = new SecretKeySpec(key.getBytes(ConstField.UTF8), KEY_MAC); mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); } catch (Exception e) { logger.error("create hmac helper failed.", e); } } public byte[] sign(byte[] content) { return mac.doFinal(content); } public boolean verify(byte[] signature, byte[] content) { try { byte[] result = mac.doFinal(content); return Arrays.equals(signature, result); } catch (Exception e) { logger.error("verify sig failed.", e); } return false; }}Copy the code
Note: HMAC instances are not secure in multithreaded environments. But for secondary classes that need to synchronize when accessing multiple threads, using ThreadLocal to cache one instance per thread avoids locking.
5.4. The AES algorithm
ES, DES, and 3DES are symmetric block encryption algorithms, and the encryption and decryption process is reversible. AES128, AES192, and AES256 are commonly used. (The JDK installed by default does not support AES256. You need to install the corresponding JCE patch to upgrade JCE1.7 and JCE1.8.)
DES encryption algorithm is a block password that encrypts data in 64-bit groups. The key length is 56 bits. The encryption and decryption algorithm is the same. DES encryption algorithm is the secret key, and the public algorithm, including encryption and decryption algorithm. In this way, only those who have the same key as the sender can read the ciphertext data encrypted by the DES encryption algorithm. Therefore, decoding the DES encryption algorithm is actually the encoding of the search key. For a key of 56 bits, the exhaustive search is 2 ^ 56 times. 3DES is a symmetric algorithm based on DES. It encrypts a piece of data for three times using three different keys.
AES encryption algorithm is the advanced encryption standard in cryptography. The encryption algorithm adopts the symmetric block cipher system, and the key length can be at least 128 bits, 192 bits, 256 bits, and the packet length is 128 bits. The algorithm should be easy to be implemented by various hardware and software. This encryption algorithm is the block encryption standard adopted by the US federal government. AES itself is designed to replace DES, with better security, efficiency and flexibility.
Java use cases:
import net.pocrd.annotation.NotThreadSafe; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; @NotThreadSafe public class AesHelper { private SecretKeySpec keySpec; private IvParameterSpec iv; public AesHelper(byte[] aesKey, byte[] iv) { if (aesKey == null || aesKey.length < 16 || (iv ! = null &&iv. length < 16)) {throw new RuntimeException(" wrong initial key "); } if (iv == null) { iv = Md5Util.compute(aesKey); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(iv); {} public AesHelper (byte [] aesKey) if (aesKey = = null | | aesKey. Length < 16) {throw new RuntimeException (" the initial key error "); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(Md5Util.compute(aesKey)); } public byte[] encrypt(byte[] data) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); result = cipher.doFinal(data); } catch (Exception e) { throw new RuntimeException(e); } return result; } public byte[] decrypt(byte[] secret) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); result = cipher.doFinal(secret); } catch (Exception e) { throw new RuntimeException(e); } return result; } public static byte[] randomKey(int size) { byte[] result = null; try { KeyGenerator gen = KeyGenerator.getInstance("AES"); gen.init(size, new SecureRandom()); result = gen.generateKey().getEncoded(); } catch (Exception e) { throw new RuntimeException(e); } return result; }}Copy the code
5.5. The RSA algorithm
RSA encryption algorithm is the most influential public key encryption algorithm and is generally regarded as one of the best public key schemes at present. RSA is the first algorithm that can be used for both encryption and digital signature. It is resistant to all cryptographic attacks known to date and has been recommended by ISO as the standard for public key data encryption.
The RSA encryption algorithm is based on a very simple number theory fact: it is easy to multiply two large prime numbers, but extremely difficult to factor their product, so the product can be exposed as an encryption key.
Java use cases:
import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class RsaHelper { private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class); private RSAPublicKey publicKey; private RSAPrivateCrtKey privateKey; Static {// Implement security.addProvider (new BouncyCastleProvider()) with BouncyCastle as the encryption algorithm; } public RsaHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public RsaHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey ! = null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey ! = null && privateKey.length > 0) { this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public RsaHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public RsaHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey ! = null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (content == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = publicKey.getModulus().bitLength() / 8 - 11; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (secret == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = privateKey.getModulus().bitLength() / 8; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("rsa decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (content == null) { return null; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (sign == null || content == null) { return false; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("rsa verify failed.", e); } return false; }}Copy the code
5.6. The ECC algorithm
ECC is also an asymmetric encryption algorithm. The main advantage is that in some cases, it uses smaller keys than other methods, such as RSA encryption algorithm, and provides an equivalent or higher level of security. One drawback is that the encryption and decryption operations take longer to implement than other mechanisms (which are CPU intensive compared to RSA).
Java use cases:
import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class EccHelper { private static final Logger logger = LoggerFactory.getLogger(EccHelper.class); private static final int SIZE = 4096; private BCECPublicKey publicKey; private BCECPrivateKey privateKey; static { Security.addProvider(new BouncyCastleProvider()); } public EccHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public EccHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey ! = null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey ! = null && privateKey.length > 0) { this.privateKey = (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (ClassCastException e) { throw new RuntimeException("", e); } catch (Exception e) { throw new RuntimeException(e); } } public EccHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public EccHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey ! = null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = SIZE; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 45)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = SIZE + 45; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size + 44) / (size + 45) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("ecc decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("ecc verify failed.", e); } return false; }}Copy the code