preface

Digital signature and information encryption are often used in front-end and back-end development. Application scenarios include user login, transaction, information communication, oAUth, etc. Different application scenarios also need to use different signature encryption algorithms, or need to match different signature encryption algorithms to achieve business goals. Here is a brief introduction to several common signature encryption algorithms and some typical applications.

The body of the

1. Digital signature A digital signature is a means of authenticating one’s identity by providing authenticated digital information. A set of digital signatures typically defines two complementary operations, one for signing and one for verification. The sender holds the private key that can represent his own identity (private key cannot be disclosed), and the receiver holds the public key corresponding to the private key, which can be used to verify his identity when receiving the information of the sender.

Note: The encryption process in the figure is different from public key encryption. The most fundamental purpose of signature is to uniquely prove the identity of the sender and prevent man-in-the-middle attack and CSRF cross-domain identity forgery. Based on this, signature algorithms are used in authentication systems such as device authentication, user authentication, and third-party authentication (their implementation methods may be different).

2. Encryption and Decryption 2.1. Encrypting Data The basic process of encryption is to process a plaintext file or data according to an algorithm to make it an unreadable code, usually called ciphertext. Through such a way, to achieve the protection of data is not stolen by the unlegal person, the purpose of reading.

2.2 decryption The reverse process of encryption is decryption, that is, the process of converting the encoded information into its original data.

Symmetric encryption and asymmetric encryption Encryption algorithms are classified into symmetric encryption and asymmetric encryption. Symmetric encryption algorithms have the same encryption key and decryption key, while asymmetric encryption algorithms have different encryption key and decryption key. In addition, there is a class of hash algorithms that do not need keys.

Common symmetric encryption algorithms include DES, 3DES, and AES, common asymmetric algorithms include RSA and DSA, and hash algorithms include SHA-1 and MD5.

3.1. Symmetric Encryption Symmetric encryption algorithm is an earlier encryption algorithm, also known as the shared key encryption algorithm. In symmetric encryption algorithms, only one key is used, and both sender and receiver use this key to encrypt and decrypt data. This requires that both encryption and decryption parties must know the encryption key beforehand.

Data encryption process: In a symmetric encryption algorithm, the data sender encrypts the plaintext (original data) and encryption key together to generate complex encrypted ciphertext for sending.

Data decryption process: If the data receiver wants to read the original data after receiving the ciphertext, it needs to use the encryption key and the reverse algorithm of the same algorithm to decrypt the encrypted ciphertext so that the ciphertext can be restored to readable text.

Asymmetric encryption Asymmetric encryption algorithm, also known as public key encryption algorithm. It requires two keys, one is called a public key, which is the public key, and the other is called a private key, which is the private key.

Because encryption and decryption use two different keys, this algorithm is called an asymmetric encryption algorithm.

If the public key is used to encrypt data, only the corresponding private key can be used to decrypt data.

If the private key is used to encrypt data, only the corresponding public key can be used to decrypt data.

Example: Party A generates a pair of keys and discloses one of them to others as a public key. Party B who has obtained the public key encrypts the confidential information with the key and then sends it to Party A. Party A then decrypts the encrypted information with another private key (private key) saved by party A.

4. Common signature encryption algorithms 4.1. MD5 Algorithm MD5 uses the hash function to generate 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.

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

4.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.

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

4.3. HMAC Algorithm HMAC is a key based Message Authentication Code. HMAC operation uses the Hash algorithm (such as MD5 and SHA1) to input a key and a Message. Generate a message digest as output.

Both the SENDER and receiver of the HMAC have a key for calculation, and a third party without this key cannot calculate the correct hash value, thus preventing data tampering.

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

Conclusion: HMAC algorithm instances are not secure in multi-threaded environment. But for secondary classes that need to synchronize when accessing multiple threads, using ThreadLocal to cache one instance per thread avoids locking.

AES/DES/3DES Algorithm AES, DES, and 3DES are symmetric block encryption algorithms. 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 Algorithm The 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.

4.4.2. 3DES algorithm is a symmetric algorithm based on DES. It encrypts a piece of data three times with three different keys, which has higher intensity.

AES The AES encryption algorithm is an advanced encryption standard in cryptography. The encryption algorithm uses a symmetric block cipher system. The key length can be at least 128 bits, 192 bits, 256 bits, and the packet length 128 bits. 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.

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

RSA encryption algorithm RSA encryption algorithm is the most influential public key encryption algorithm, and is generally considered to be one of the best public key schemes. 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.

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 { Security.addProvider(new BouncyCastleProvider()); } 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

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 a similar 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).

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

5. Comparison of various encryption algorithms

5.1. Comparison of hashing algorithms



5.2. Comparison of symmetric encryption algorithms



5.3. Comparison of asymmetric encryption algorithms



5.4. Symmetric algorithm and asymmetric encryption algorithm

5.4.1. Symmetric algorithm

Key management: more difficult, not suitable for the Internet, generally used for internal systems

Security: medium

Encryption speed: several orders of magnitude faster (software encryption and decryption speed is at least 100 times faster, can encrypt and decrypt several M bits of data per second), suitable for large data encryption and decryption processing

Asymmetric algorithm Key management: easy key management Security: high encryption speed: slow, suitable for small amount of data encryption and decryption or data signature

This paper introduces digital signature, encryption and decryption, symmetric encryption and asymmetric encryption, and then introduces MD5, SHA-1, HMAC, DES/AES, RSA and ECC encryption algorithms and code examples in detail.