乐趣区

关于rsa:编程中常用的加密算法

前言

编程中常见的加密算法有以下几种,它们在不同场景中别离有利用。除信息摘要算法外,其它加密形式都会须要密钥。

  • 信息摘要算法
  • 对称加密算法
  • 非对称加密算法

密钥

密钥 (key,又常称 金钥 )是指某个用来实现 加密 解密 完整性验证 等密码学利用的机密信息。

密钥分类

  • 加解密中的密钥:对称加密中共享雷同的密钥,非对称加密中分 公钥 私钥,公钥加密私钥解密。
  • 音讯认证码和数字签名中的密钥:在音讯认证码中,音讯发送方和接管方应用共享密钥进行认证。在数字签名中,签名应用私钥,而验证应用公钥。
  • 会话密钥和主密钥:每次通信只应用一次的密钥称为会话密钥(session key)。绝对于会话密钥,重复使用的密钥称为主密钥(master key)。

密钥和明码

明码个别是由用户生成,具备可读性,能够记忆和存储,罕用于软件治理,而密钥是供实现加密算法的软件应用,不须要具备可读性(不过在编程中为了不便浏览都进行 Base64)。咱们也能够通过明码来生成密钥。

密钥治理

  • 生成密钥:能够用随机数生成密钥,也能够用口令生成密钥。
  • 配送密钥:可采纳当时共享密钥、应用密钥调配核心、应用公钥明码、应用 Diffie-Hellman 密钥替换。
  • 更新密钥
  • 保留密钥
  • 作废密钥

密钥生成

jdk 中 jce (Java Cryptography Extension) 蕴含了加密相干的所有 API。

生成对称加密算法的密钥

    public static SecretKey generateKey(int keySize) {
        KeyGenerator keyGenerator;
        try {keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(keySize);
            return keyGenerator.generateKey();} catch (NoSuchAlgorithmException e) {
            // ignore
            return null;
        }
    }

生成对称非对称加密算法的密钥

    /**
     * 生成非对称密钥对
     *
     * @param keySize 密钥大小
     * @param random  指定随机起源,默认应用 JCAUtil.getSecureRandom()
     * @return 非对称密钥对
     * @throws NoSuchAlgorithmException NoSuchAlgorithm
     */
    public static PPKeys genKeysRSA(int keySize, SecureRandom random) throws NoSuchAlgorithmException {KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        if (null != random) {generator.initialize(keySize, random);
        } else {generator.initialize(keySize);
        }
        KeyPair pair = generator.generateKeyPair();
        PPKeys keys = new PPKeys();
        PublicKey publicKey = pair.getPublic();
        PrivateKey privateKey = pair.getPrivate();
        keys.setPublicKey(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
      keys.setPrivateKey(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        return keys;
    }

密钥协商Diffie-Hellman

密钥协商是一种协定,两方或多方在通过该协定建设雷同的共享密钥,而后通信内容进行对称加密传输,而不须要替换密钥。

大抵过程:每一方生成一个公私钥对并将公钥分发给其它方,当都取得其余方的公钥正本后就能够离线计算共享密钥。

Java 中提供了 KeyAgreement 能够实现密钥协商。

  • Alice 和 Bob 别离用他们的私钥初始化本人的密钥协商对象 KeyAgreement,调用init() 办法;
  • 而后将通信的每一方的公钥 传入执行 doPhase(Key key, boolean lastPhase)
  • 各方生成共享密钥 generateSecret()
    public static void diffieHellman() throws Exception {AlgorithmParameterGenerator dhParams = AlgorithmParameterGenerator.getInstance("DH");
        dhParams.init(1024);
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
        keyGen.initialize(dhParams.generateParameters().getParameterSpec(DHParameterSpec.class), new SecureRandom());

        KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
        KeyPair alicePair = keyGen.generateKeyPair();
        KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
        KeyPair bobPair = keyGen.generateKeyPair();

        aliceKeyAgree.init(alicePair.getPrivate());
        bobKeyAgree.init(bobPair.getPrivate());

        aliceKeyAgree.doPhase(bobPair.getPublic(), true);
        bobKeyAgree.doPhase(alicePair.getPublic(), true);

       boolean agree = Base64.getEncoder().encodeToString(aliceKeyAgree.generateSecret()).equals(Base64.getEncoder().encodeToString(bobKeyAgree.generateSecret())
        );
        System.out.println(agree);
    }

信息摘要算法

信息摘要算法又叫 加密散列算法 ,加密过程不须要密钥,常见的加密散列算法有MD 系列SHA 系列

一个现实的加密散列函数应该具备以下个性:

  • 任何信息传入后,输入的总是长度固定;
  • 音讯摘要看起来是“随机的”,这样依据原始信息就很难揣测出值;
  • 好的散列函数碰撞概率应该极低,也就是不同信息传入后失去雷同值的概率;

MD 系列

MD5 信息摘要算法(MD5 Message-Digest Algorithm),一种被宽泛应用的加密散列函数,输入出一个 128 位(16 字节)的散列值(hash value),MD5 最后设计为加密散列函数,而目前发现它存在大量破绽,所以不倡议间接用作加密,不过在非加密场景下如:数据完整性校验,文件完整性校验它依然有宽泛的利用。

    public static String md5(String content) {
        try {MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(content.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(bytes);
        } catch (final NoSuchAlgorithmException e) {throw new IllegalArgumentException(e);
        }
    }

SHA 系列

平安散列算法 (Secure Hash Algorithm,缩写为 SHA)是一个加密散列函数家族,是 FIPS(美国联邦信息处理规范) 所认证的平安散列算法。能计算出一个数字音讯所对应到的,长度固定的字符串(又称音讯摘要)的算法。且若输出的音讯不同,它们对应到不同字符串的机率很高。

它们别离蕴含 SHA-0、SHA-1、SHA-2、SHA-3,其中 SHA-0、SHA-1 输入长度是 160 位,SHA-2 蕴含 SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256,咱们平时罕用 SHA-256

    public static String sha256(String content) {
        try {MessageDigest digest = MessageDigest.getInstance("SHA-256);
            byte[] bytes = digest.digest(content.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(bytes);
        } catch (final NoSuchAlgorithmException e) {throw new IllegalArgumentException(e);
        }
    }

对称加密算法

对称加密算法,单方持有雷同密钥进行加解密,常见的对称加密算法:DES 3DES AES128 AES192 AES256。了解对称加密须要先明确上面几个概念:

  • 分组明码模式:将明文切割进行加密,再将密文拼接到一起。比方 AES 中会将明文数据切割为大小 16 字节的数据块,最初一块不够 16 字节时,应用 Padding 模式进行补充。
  • 填充(Padding):它有三种模式 PKCS5、PKCS7 和 NOPADDING,PKCS5 用短少的字节数来填充,比方短少 5 个字节就填充 5 个数字 5,PKCS7 短少的字节数用 0 来填充。如果数据刚好是 16 的整数倍,PKCS5 和 PKCS7 会再补充一个 16 字节数据来辨别填充和无效数据,NOPADDING 模式不须要填充。
  • 初始化向量:初始向量 IV 的作用是使加密更加安全可靠,在分组明码模式下 IV 大小对应数据块长度。
  • 加密模式:四种加密模式别离是:ECB(电子密码本模式)、CBC(明码分组链接模式)、CFB、OFB。ECB 模式是仅仅应用明文和密钥来加密数据,所以该模式下不须要 Padding,安全性也较弱,CBC 模式数据分块并且应用传入 IV 顺次进行异或操作,安全性也绝对较高,所以目前个别都抉择 CBC 模式。
  • 加密密钥:不同加密算法密钥长度不同,比方:DES 默认长度 56 位,3DES 默认长度 168 位,也反对 128 位,AES 默认 128 位,也反对 192 位,256 位。咱们个别依据明码生成密钥,明码长度须要满足算法密钥长度。

DES

DES 是对称加密算法畛域中的典型算法,因为密钥默认长度为56 bit,所以明码长度须要大于 8 byteDESKeySpec 取前 8 byte 进行密钥制作。

 public static String encryptDES(byte[] content, String password) {
        try {SecureRandom random = new SecureRandom();
            DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
            return Base64.getEncoder().encodeToString(cipher.doFinal(content));
        } catch (Exception e) {throw new RuntimeException(e);
        }
    }

    public static String decryptDES(String content, String password) throws Exception {SecureRandom random = new SecureRandom();
        DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
        return new String(cipher.doFinal(Base64.getDecoder().decode(content)));
    }

3DES

3DES(即 Triple DES)。是 DES 算法的增强,它应用 3 条 56 位的密钥对数据进行三次加密。它以 DES 为根本模块,通过组合分组办法设计出分组加密算法。比起最后的 DES,3DES 更为平安。密钥默认长度 168 bit,明码须要大于24 byte,IV 是 8 byte 的随机数字和字母数组。


    public static String encrypt3DESECB(String content, String key, String iv) {
        try {IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            DESedeKeySpec dks = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
            SecretKey secretkey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretkey, ivSpec);
            return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {throw new RuntimeException(e);
        }
    }

    public static String decrypt3DESECB(String content, String key, String iv) {
        try {IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            DESedeKeySpec dks = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
            SecretKey secretkey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretkey, ivSpec);
            return new String(cipher.doFinal(Base64.getDecoder().decode(content)), StandardCharsets.UTF_8);
        } catch (Exception e) {throw new RuntimeException(e);
        }
    }

AES

AES 高级数据加密规范,可能无效抵挡已知的针对 DES 算法的所有攻打,默认密钥长度为128 bit,还能够供选择 192 bit256 bitAES-128 AES-192 AES-256

默认 AES-128,应用 PBEKeySpec 生成固定大小的密钥。

public static String encryptAES128(String plainText, String password, String salt) throws Exception {SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);
        // AES-128 密钥长度为 128bit
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(),
          saltBytes,
          1000,
          128
        );
        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        AlgorithmParameters params = cipher.getParameters();
        IvParameterSpec iv = params.getParameterSpec(IvParameterSpec.class);

        cipher.init(Cipher.ENCRYPT_MODE, secret, iv);
        byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        String encodedText = Base64.getEncoder().encodeToString(encryptedTextBytes);
        String encodedIV = Base64.getEncoder().encodeToString(iv.getIV());
        String encodedSalt = Base64.getEncoder().encodeToString(saltBytes);
        return encodedSalt + "." + encodedIV + "." + encodedText;
    }


    public static String decryptAES128(String encryptedText, String password) throws Exception {String[] fields = encryptedText.split("\\.");
        byte[] saltBytes = Base64.getDecoder().decode(fields[0]);
        byte[] ivBytes = Base64.getDecoder().decode(fields[1]);
        byte[] encryptedTextBytes = Base64.getDecoder().decode(fields[2]);

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(),
          saltBytes,
          1000,
          128
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
        byte[] decryptedTextBytes;
        try {decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
            return new String(decryptedTextBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {throw new RuntimeException(e);
        }
    }

应用 AES-256 时可能会呈现上面异样:

 java.security.InvalidKeyException: Illegal key size

JDK 1.8.0_161 及以上版本默认曾经启用有限强度加密:

    static {java.security.Security.setProperty("crypto.policy", "unlimited");
    }

JDK 1.8.0_161 以前版本须要手动装置 jce 策略文件(下载地址)

非对称加密算法

非对称加密应用一对密钥,公钥用作加密,私钥则用作解密。对于密钥大小,截至 2020 年,公开已知的最大 RSA 密钥是破解的是 829 位的 RSA-250,倡议至多应用 2048 位密钥。

    public static String encrypt(byte[] publicKey, String plainText) {X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf;
        try {kf = KeyFactory.getInstance("RSA");
            PublicKey publicKeySecret = kf.generatePublic(keySpec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKeySecret);
            byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
            return new String(Base64.getEncoder().encode(encryptedBytes));
        } catch (Exception e) {log.error("Rsa encrypt error", e);
            throw new RuntimeException(e);
        }
    }

    public static String decrypt(byte[] privateKey, String encryptedText) {PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf;
        try {kf = KeyFactory.getInstance("RSA");
            PrivateKey privateKeySecret = kf.generatePrivate(keySpec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKeySecret);
            return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)), StandardCharsets.UTF_8);
        } catch (Exception e) {log.error("Rsa decrypt error", e);
            throw new RuntimeException(e);
        }
    }
退出移动版