【Android】Encryption algorithm related knowledge points

It is mainly used to transmit sensitive data such as passwords in plain text, and the sensitive data stored locally is accessed by encryption and decryption.
Java encryption algorithms are mainly divided into: symmetric encryption, asymmetric encryption, digest algorithm.

Recommended articles are as follows:

Common encryption algorithm code examples
Android commonly used encryption methods
Encryption method classification :

1. Symmetric encryption algorithm (same encryption and decryption keys, DES, 3DES, AES)

The key management is complex, not suitable for the Internet, and is generally used in internal systems; in security; the encryption speed is extremely fast, suitable for encryption processing of large amounts of data; the process of encryption and decryption is reversible.

name key name running speed safety LF
FROM 56th place faster Low middle
3DES 112-bit or 168-bit slow middle high
AES 128th, 192nd, 256th quick high Low
  • DES algorithm: The DES encryption algorithm is a block cipher that encrypts data in 64-bit groups. Its key length is 56 bits, and the same algorithm is used for encryption and decryption. The DES encryption algorithm keeps the key secret, while the public algorithm includes encryption and decryption algorithms. In this way, only the person who has the same key as the sender can decipher the ciphertext data encrypted by the DES encryption algorithm. Therefore, deciphering the DES encryption algorithm is actually the encoding of the search key. For a key of 56-bit length, the number of operations is 2 ^ 56 if the exhaustive method is used to search.
  • 3DES algorithm: It is a symmetric algorithm based on DES, which encrypts a piece of data three times with three different keys, with higher strength.
  • AES algorithm: The AES encryption algorithm is an advanced encryption standard in cryptography. The encryption algorithm adopts a symmetric block cipher system. The minimum supported key length is 128 bits, 192 bits, and 256 bits, and the block length is 128 bits. Hardware and software implementation. This encryption algorithm is the block encryption standard adopted by the U.S. federal government. It is designed to replace DES and has better security, efficiency and flexibility.

@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("错误的初始密钥");
        }
        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("错误的初始密钥");
        }
        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;
    }
}

2. Asymmetric encryption algorithm (public key, private key, encryption and decryption are different but a pair, RSA, ECC)

The key management is easy, the security is high, and the encryption speed is relatively slow, which is suitable for small data encryption and decryption or data signature.

name maturity safety calculating speed LF
RSA high high middle middle
ETC high high slow high
  • RSA algorithm: RSA encryption algorithm is currently the most influential public key encryption algorithm, and is generally considered to be 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 so far and has been recommended by ISO as the standard for public key data encryption. The RSA encryption algorithm is based on a very simple fact of number theory: it is easy to multiply two large prime numbers together, but it is extremely difficult to factor the product, so the product can be made public as an encryption key.

@NotThreadSafe
public class RsaHelper {
    private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class);
    private RSAPublicKey publicKey;
    private RSAPrivateCrtKey privateKey;
 
    static {
        Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作为加密算法实现
    }
 
    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;
    }
}
  • ECC algorithm: ECC is also an asymmetric encryption algorithm, the main advantage is that in some cases, it uses a smaller key than other methods, such as the RSA encryption algorithm, providing a comparable or higher level of security. However, one disadvantage is that the implementation of encryption and decryption operations takes longer than other mechanisms (compared to the RSA algorithm, which is CPU-intensive).

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;
    }
}

3. Digest algorithm hash algorithm (variable length and fixed length, cannot be restored, MD5, SHA, CRC)

It is irreversible and cannot be decrypted. So it can only be regarded as a one-way encryption algorithm

name safety speed
SHA-1 high slow
MD5 middle quick
  • MD5 (Message-Digest Algorithm 5 (Message-Digest Algorithm 5)): Hash function is used, and its typical application is to generate a message digest for a piece of information to prevent tampering. No matter how long the input is, MD5 will output a string of length 128bits (usually 32 characters in hexadecimal).

public static final byte[] computeMD5(byte[] content) {
    try {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        return md5.digest(content);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}
  • SHA1 (Secure Hash Algorithm 1): It is more secure than MD5, but slower than MD5. For messages whose length is less than 2^64 bits, SHA1 will generate a 160-bit message digest.

public static byte[] computeSHA1(byte[] content) {
    try {
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        return sha1.digest(content);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}
  • HMAC (Hash Message Authentication Code): Hash-based Message Authentication Code related to key, HMAC operation uses hash algorithm (MD5, SHA1, etc.), with a key and a message as input, Generates a message digest as output.
    The HMAC sender and receiver have the key for calculation, and the third party without the key cannot calculate the correct hash value, which can prevent data from being tampered with.

@NotThreadSafe
public class HMacHelper {
    private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class);
    private Mac mac;
 
    /**
     * MAC算法可选以下多种算法
     * HmacMD5/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;
    }
}

Fourth, Base64

It is a data encoding method. Although it is reversible, its encoding method is public and does not matter encryption. It is a kind of original binary data that uses 4 bytes of text to represent 3 bytes of data.

  • It is often used in network transmission. In some text-based protocols, if you need to transmit pictures or files, etc., we know that the storage format of pictures is binary data, not text format. We must encode the binary data into text format. At this time That's where Base64 comes in handy.
  • Also, since only ASCII characters can be used in some systems. Base64 is a method used to convert non-ASCII character data into ASCII characters.
  • Base64 encoding tends to be larger than the original data, so it does not compress the data.

Guess you like

Origin blog.csdn.net/ahou2468/article/details/123406655