# 加密算法的分类

# 是否需要key

  • 不基于key的有:
    • Base64算法
    • MD5
  • 基于key的有:
    • 对称加密算法
    • 非对称加密算法
    • 数字签名算法
    • 数字证书
    • HMAC(Hash-based Message Authentication Code,hash消息认证码)
    • RC4(对称加密)

# 加密算法是否可逆

  • 单向加密
    • MD5
    • SHA
    • HMAC
  • 非单向加密
    • BASE64
    • 对称加密算法
    • 非对称加密算法
    • 数字签名算法
    • 数字证书

# JAVA常见加解密示例

# MD5

  • 核心功能位于 java.security.*包
package com.automannn.demo.security;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class MD5Test {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        String originMsg = "originMsg";
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        System.out.println(new String(md5.digest(originMsg.getBytes(StandardCharsets.UTF_8))));
        //BigInteger函数用于将 8位的字符串 转为 16位的 hex值,得到字符串形式的hash值
        BigInteger digest = new BigInteger(md5.digest(originMsg.getBytes(StandardCharsets.UTF_8)));
        System.out.println(digest.toString(16));
    }
}

# BASE64

  • 核心功能位于 java.util.Base64包
package com.automannn.demo.security;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class Base64Test {
    public static void main(String[] args) {
        String originMessage = "originMessage";
       byte[] encodedMsg =  Base64.getEncoder().encode(originMessage.getBytes(StandardCharsets.UTF_8));
       byte[] decodedMsg = Base64.getDecoder().decode(encodedMsg);
        System.out.println(new String(decodedMsg));
    }
}

# DES

  • 核心方法位于javax.crypto.*包
package com.automannn.demo.security;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class DESTest {
    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        String originMsg = "OriginMessage";
        //加密密钥,长度至少要大于8位
        String password = "aaddffee1";

        //=================加密=======================
        DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKey);
        Cipher cipher = Cipher.getInstance("DES");
        //用密匙初始化 cipher对象
        cipher.init(Cipher.ENCRYPT_MODE,secretKey,new SecureRandom());
       byte[] encodedContent= cipher.doFinal(originMsg.getBytes(StandardCharsets.UTF_8));
        System.out.println(new String(encodedContent));
        //BigInteger函数用于将 8位的字符串 转为 16位的 hex值,得到字符串形式的值
        BigInteger digest = new BigInteger(encodedContent);
        System.out.println(digest.toString(16));

        //=========================解密=============
        cipher.init(Cipher.DECRYPT_MODE,secretKey,new SecureRandom());
        byte[] decodedContent = cipher.doFinal(encodedContent);
        System.out.println(new String(decodedContent));
    }
}

# RSA

  • 核心方法位于javax.crypto.*包
package com.automannn.demo.security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class RSATest {
    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
        String originMsg = "originMessage";

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        // RSA算法的模长,即为  最大加密数据的大小
        keyPairGenerator.initialize(1024);

       KeyPair keyPair= keyPairGenerator.generateKeyPair();
       PublicKey publicKey =  keyPair.getPublic();
       PrivateKey privateKey = keyPair.getPrivate();

       //===============公钥加密================
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE,publicKey);
        //将模长 转为字节数
        int modulusSize = ((RSAPublicKey)publicKey).getModulus().bitLength()/8;
        //PKCS Padding 长度为11字节,实际要加密的数据要去掉
        int maxSingleSize = modulusSize -11;
        //切分数组,分段加密
        byte[][] dataArray = splitArray(originMsg.getBytes(StandardCharsets.UTF_8),maxSingleSize);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        for (byte[] arr: dataArray){
            outputStream.write(cipher.doFinal(arr));
        }
        String encodedContent = Base64.getEncoder().encodeToString(outputStream.toByteArray());
        System.out.println(encodedContent);

        //===============私钥解密===========================
        cipher.init(Cipher.DECRYPT_MODE,privateKey);
        //获取加密算法的模长,转为字节数
        int priModulusSize = ((RSAPrivateKey)privateKey).getModulus().bitLength()/8;
        byte[] encodedData = outputStream.toByteArray();
        byte[][] priSplitArrays = splitArray(encodedData,priModulusSize);

        ByteArrayOutputStream desOutputStream = new ByteArrayOutputStream();
        //分段解密
        for (byte[] arr: priSplitArrays){
            desOutputStream.write(cipher.doFinal(arr));
        }
        String decodedData = new String(desOutputStream.toByteArray());
        System.out.println(decodedData);
    }

    //按照指定长度切分数组
    private static byte[][] splitArray(byte[] data, int len) {
        int dataLen = data.length;
        if (dataLen <= len) {
            return new byte[][]{data};
        }
        byte[][] result = new byte[(dataLen-1)/len + 1][];
        int resultLen = result.length;
        for (int i = 0; i < resultLen; i++) {
            if (i == resultLen - 1) {
                int slen = dataLen - len * i;
                byte[] single = new byte[slen];
                System.arraycopy(data, len * i, single, 0, slen);
                result[i] = single;
                break;
            }
            byte[] single = new byte[len];
            System.arraycopy(data, len * i, single, 0, len);
            result[i] = single;
        }
        return result;
    }
}

# 字符串转为公私钥证书

package com.automannn.demo.security;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class RSAStrUtil {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
       KeyPair keyPair= keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey  =keyPair.getPrivate();
        String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());

        //公钥从字符串复原为对象
        RSAPublicKey rsaPublicKey = getPublicKey(publicKeyStr);
        System.out.println(rsaPublicKey);

        //私钥从字符串复原为对象
        RSAPrivateKey rsaPrivateKey = getPrivateKey(privateKeyStr);
        System.out.println(rsaPrivateKey);

    }

    //Java中 RSAPublicKeySpec, X509EncodedKeySpec 支持生成RSA公钥
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        return (RSAPublicKey) keyFactory.generatePublic(x509EncodedKeySpec);
    }

    //java中, RSAPrivateKeySpec, PKCS8EncodedKeySpec支持生成 RSA私钥
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);

        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }


}

# SHA

  • 核心方法位于 java.security.* 包
package com.automannn.demo.security;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class SHATest {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        String originMsg = "originMessage";

        MessageDigest sha = MessageDigest.getInstance("SHA");
        byte[] sha_byte = sha.digest(originMsg.getBytes(StandardCharsets.UTF_8));
        StringBuilder hexValue = new StringBuilder();
        for (byte b: sha_byte){
            //将每个字节转为十六进制字符串: byte类型的数据最高位为符号位,通过和 0xff与操作,转为int类型的正整数
            String toHexString = Integer.toHexString(b&0xff);
            hexValue.append(toHexString.length()==1?"0"+toHexString:toHexString);
        }
        System.out.println(hexValue);

        BigInteger bigInteger = new BigInteger(sha_byte);

        System.out.println(bigInteger.toString(16));

    }
}

# HMAC

  • 核心功能基于 javax.crypto.*包
package com.automannn.demo.security;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * @author automannn
 * @Date 2022/7/24
 */
public class HMACTest {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        String password = "adfdfsd";
        String originMsg = "originMessage";
        SecretKey secretKey = new SecretKeySpec(password.getBytes(StandardCharsets.UTF_8),"HmacMD5");
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        byte[] encodedMsg = mac.doFinal(originMsg.getBytes(StandardCharsets.UTF_8));
        System.out.println(Base64.getEncoder().encodeToString(encodedMsg));

    }
}

# 在不引入三方库的情况下,JDK支持有限的摘要算法

  • HmacMD5
  • HmacSHA1
  • HmacSHA224
  • HmacSHA256
  • HmacSHA384
  • HmacSHA512

# JavaSecurity程序常见的加密算法

# 顶级接口passwordEncoder

public interface PasswordEncoder {
  /*xxx: 加密,向数据库存储密码时使用*/
  String encode(CharSequence rawPassword);

  /*xxx: 判断登陆时输入的密码是否匹配*/
  boolean matches(CharSequence rawPassword, String encodedPassword);
}

# BCrypt

  • 密码哈希函数,它可以保证加密速度在一个特定范围内
  • 基于blowfish,于1999年提出
public class BCryptPasswordEncoder implements PasswordEncoder {
  private final int strength;

  private final SecureRandom random;

  public String encode(CharSequence rawPassword) {
    String salt;
    if (strength > 0) {
      if (random != null) {
        salt = BCrypt.gensalt(strength, random);
      }
      else {
        salt = BCrypt.gensalt(strength);
      }
    }
    else {
      salt = BCrypt.gensalt();
    }
    /*xxx: 根据盐值加密*/
    return BCrypt.hashpw(rawPassword.toString(), salt);
  }

  public boolean matches(CharSequence rawPassword, String encodedPassword) {
    /*xxx: rawPassword代码密码原文*/
    return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
  }
}

# 代理加密

public class DelegatingPasswordEncoder implements PasswordEncoder {
  private static final String PREFIX = "{";
  private static final String SUFFIX = "}";
  private final Map<String, PasswordEncoder> idToPasswordEncoder;
  private final String idForEncode;

  @Override
  public String encode(CharSequence rawPassword) {
    return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
  }

  @Override
  public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
    //xxx: 省略其它抽象...
    PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
    return delegate.matches(rawPassword, encodedPassword);
  }
}

# NoOp

public final class NoOpPasswordEncoder implements PasswordEncoder {
    
  public String encode(CharSequence rawPassword) {
    return rawPassword.toString();
  }

  public boolean matches(CharSequence rawPassword, String encodedPassword) {
    return rawPassword.toString().equals(encodedPassword);
  }
}

# 国密(商用密码->商密->SM)

  • 国家密码局发布的,一系列由我国自主研发的 商用密码(Shangyong Mima ->SM)标准算法;

# SM1(分组加密算法)

  • SM1算法没有公开,只能集成在芯片中
  • 该算法是对称加密算法

# SM2(椭圆曲线公钥密码算法)

  • 国家密码局于2010年12月公布
  • 非对称加密算法,使用公钥加密,私钥解密,安全性和运算速度方面,优于RSA算法

# SM3(杂凑算法)

  • 不可逆加密算法,类似于md5,常用于签名

# SM4(对称算法)

  • 对称加密算法,可用于替代 DES/AES等国际算法

# SM7(对称密码)

  • 适用于门禁类,票务类,支付与通卡类应用

# SM9(标志密码算法)

  • 非对称加密算法
  • 通过设备标志作为公钥,实现数据加密、身份认证等应用