目录

常用加密算法对比

Cipher 密码加密概述

Cipher 常量与常用方法

Hello World 快速入门

RSA 非对称加密详解

JCE 无限强度权限策略文件

原生 AES、DES、RSA 加解密


常用加密算法对比

https://blog.csdn.net/wangmx1993328/article/details/83892385#常用加密算法对比

Cipher 密码加密概述

1、javax.crypto.Cipher 类提供加密和解密的功能,它构成了 Java Cryptography Extension (JCE) —— Java 加密扩展框架的核心。这些都是 Java JDK 原生的 API,不是第三方的。

2、Cipher 的 getInstance(String transformation) 方法可以获取实例,参数 transformation 表示转换名称,包括:加密算法/反馈模式/填充方案加密算法是必选项,反馈模式与填充方案可以不写使用默认值。如:

Cipher cipher = Cipher.getInstance("AES");
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");

3、Cipher  支持 4 种加密算法:AES、DES、DESede、RSA。

Cipher.getInstance 实例化时转换名称参数可选值如下
AES/CBC/NoPadding
AES/CBC/PKCS5Padding
AES/ECB/NoPadding
AES/ECB/PKCS5Padding

1、加密算法有 4 种:AES、DES、DESede、RSA

2、反馈模式有两种:CBC(有向量模式)、ECB(无向量模式)

3、填充方案有 5 种:NoPadding 、PKCS5Padding、PKCS1Padding 、OAEPWithSHA-1AndMGF1Padding、OAEPWithSHA-256AndMGF1Padding

4、NoPadding 填充方案: 加密内容不足8位时用0补足8位, Cipher类不提供补位功能,需自己实现代码给加密内容添加0, 如{65,65,65,0,0,0,0,0}

5、PKCS5Padding 填充方案: 加密内容不足8位用余位数补足8位, 如{65,65,65,5,5,5,5,5}或{97,97,97,97,97,97,2,2}.

DES/CBC/NoPadding
DES/CBC/PKCS5Padding
DES/ECB/NoPadding
DES/ECB/PKCS5Padding

DESede/CBC/NoPadding
DESede/CBC/PKCS5Padding
DESede/ECB/NoPadding
DESede/ECB/PKCS5Padding

RSA/ECB/PKCS1Padding
RSA/ECB/OAEPWithSHA-1AndMGF1Padding
RSA/ECB/OAEPWithSHA-256AndMGF1Padding

Cipher 常量与常用方法

javax.crypto.Cipher 常量字段
字段类型字段名称字段值描述
static intENCRYPT_MODE1常数用于将密码初始化为加密模式。
static intDECRYPT_MODE2常数用于将密码初始化为解密模式。
static intWRAP_MODE3常数用于将密码初始化为密钥包装模式
static intUNWRAP_MODE4常数用于将密码初始化为密钥解包模式。
static intPUBLIC_KEY1用于指示待解除密钥的常数是“公钥”。
static intPRIVATE_KEY2用于指示要解除密钥的常数是“私钥”。
static intSECRET_KEY3用于指示要解开的密钥的常数是“密钥”。
javax.crypto.Cipher 常用 API
方法描述
Cipher getInstance(String transformation)
Cipher getInstance(String transformation,Provider provider)
Cipher getInstance(String transformation,String provider)

实例化 Cipher 对象。

1、transformation 转换名称,格式:加密算法/反馈模式/填充方案

2、provider 供应商

init(int opmode,Key key)
init(int opmode,Key key,AlgorithmParameters params)
init(int opmode,Key key,AlgorithmParameterSpec params)
init(int opmode,Key key,AlgorithmParameters params,SecureRandom random)
init(int opmode,Key key,AlgorithmParameterSpec params,SecureRandom random)
init(int var1, Key var2, SecureRandom var3)
init(int opmode,Certificate certificate)
init(int opmode,Certificate certificate,SecureRandom random)

初始化 Cipher

1、opmode 对应上面常量:解密、加密、解包、模式、包装。

2、key :密匙,可以使用 SecretKeySpec、KeyGenerator 和 KeyPairGenerator 创建。其中
SecretKeySpec 和 KeyGenerator 支持 AES,DES,DESede 三种加密算法创建密匙,KeyPairGenerator支持 RSA 加密算法创建密匙。
3、params :使用 CBC 模式时必须传入该参数.

update(byte[] input)
update(byte[] input,int off,int len)
输入给定的字节数组完成更新

update(byte[] input,int off,int len,byte[] output)
update(byte[] input,int off,int len,byte[] output,int outputoffset)

将更新结果输出到参数中
update(ByteBuffer input,ByteBuffer output)使用缓冲的方式更新

byte[] wrap(Key key)

包装密钥
Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)

解包装

wrappedKeyType 对应常量 PRIVATE_KEY、PUBLIC_KEY、SECRET_KEY

 byte[] getIV()返回缓冲区中的初始化向量

int getMaxAllowedKeyLength(String transformation)

返回指定转换的最大密钥长度

int getBlockSize()

返回块大小(以字节为单位),如果底层算法不是块密码,则为 0.

分组加密中,每一组都有固定的长度,称为块.

int getOutputSize(int inputLen)获取输出缓冲区字节长度
 String getAlgorithm()返回此 Cipher 对象的算法名称。
byte[] doFinal()完成多部分加密或解密操作,这取决于该密码如何初始化。
返回的 byte 数组,如果直接使用 new String(b) 封装成字符串,则会出现乱码。

Hello World 快速入门

源码中都有详细说明,这里重点强调几点;

1、使用 CBC 有向量模式时,cipher.init 必须传入 {@link AlgorithmParameterSpec}-算法参数规范。如果使用的是 ECB-无向量模式,那么 cipher.init 则加解密都不需要传  {@link AlgorithmParameterSpec} 参数.

2、生成密钥 SecretKey 与 算法参数规范 AlgorithmParameterSpec 的 key ,AES加密算法时必须是 16 个字节,DES 时必须是 8 字节.

3、加/解密都是调用 Cipher.doFinal(byte[] content) 方法,具体取决于 cipher.init 时 使用的是 Cipher.ENCRYPT_MODE 加密模式,还是 Cipher.DECRYPT_MODE 解密模式.

4、下面演示的是 AES 加密算法,DES 加密算法使用也是同理,区别在于参数:secret_key、vector_key : AES 时必须是 16 个字节,DES 时必须是 8 字节.

public class CipherUtils {
    /**
     * AES/CBC/PKCS5Padding 加密
     *
     * @param content    :待加密的内容.
     * @param secret_key :用于生成密钥的 key,自定义即可,加密与解密必须使用同一个,如果不一致,则抛出异常
     * @param vector_key 用于生成算法参数规范的 key,自定义即可,加密与解密必须使用同一个,如果不一致,解密的内容可能会造成与源内容不一致.
     *                   <p>
     *                   1、secret_key、vector_key: AES 时必须是 16 个字节,DES 时必须是 8 字节.
     *                   2、secret_key、vector_key 值不建议使用中文,如果是中文,注意一个汉字是3个字节。
     *                   </p>
     * @return 返回 Cipher 加密后的数据,对加密后的字节数组用 Base64 进行编码转成了可视字符串,如 7giH2bqIMH3kDMIg8gq0nY
     * @throws Exception
     */
    public static String encrypt(String content, String secret_key, String vector_key) throws Exception {
        //实例化 Cipher 对象。使用:AES-高级加密标准算法、CBC-有向量模式、PKCS5Padding-填充方案:(加密内容不足8位时用余位数补足8位)
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //使用 SecretKeySpec(byte[] key, String algorithm) 创建密钥. 算法要与 Cipher.getInstance 保持一致.
        SecretKey secretKey = new SecretKeySpec(secret_key.getBytes(), "AES");
        /**
         * init(int opMode,Key key,AlgorithmParameterSpec params):初始化 Cipher,
         * 1、Cipher.ENCRYPT_MODE 表示加密模式
         * 2、key 表示加密密钥
         * 3、params 表示算法参数规范,使用 CBC 有向量模式时,必须传入,如果是 ECB-无向量模式,那么可以不传
         * 4、所有参数规范都必须实现 {@link AlgorithmParameterSpec} 接口.
         */
        IvParameterSpec parameterSpec = new IvParameterSpec(vector_key.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
        /**
         * byte[] doFinal(byte[] content):对 content 完成加密操作,如果 cipher.init 初始化时使用的解密模式,则此时是解密操作.
         * 返回的是加密后的字节数组,如果直接 new String(byte[] bytes) 是会乱码的,可以借助 BASE64 转为可视字符串,或者转成 16 进制字符
         */
        byte[] encrypted = cipher.doFinal(content.getBytes());
        //BASE64Encoder.encode:BASE64 对字节数组内容进行编码,转为可视字符串,这样方便存储和转换.
        String base64Encode = new BASE64Encoder().encode(encrypted);
        return base64Encode;
    }

    /**
     * AES/CBC/PKCS5Padding 解密
     *
     * @param base64Encode :待解密的内容,因为加密时使用了 Base64 进行了编码,所以这里传入的也是 Base64 编码后的可视化字符串
     * @param secret_key   :用于生成密钥的 key,自定义即可,加密与解密必须使用同一个,如果不一致,则抛出异常
     * @param vector_key   用于生成算法参数规范的 key,自定义即可,加密与解密必须使用同一个,如果不一致,解密的内容可能会造成与源内容不一致.
     *                     <p>
     *                     1、secret_key、vector_key:AES 时必须是 16 个字节,DES 时必须是 8 字节.
     *                     2、secret_key、vector_key 值不建议使用中文,如果是中文,注意一个汉字是3个字节。
     *                     </p>
     * @return
     * @throws Exception
     */
    public static String decrypt(String base64Encode, String secret_key, String vector_key) throws Exception {
        //实例化 Cipher 对象。加密算法/反馈模式/填充方案,解密与加密需要保持一致.
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //创建密钥。算法也要与实例化 Cipher 一致.
        SecretKey secretKey = new SecretKeySpec(secret_key.getBytes(), "AES");
        //有向量模式(CBC)需要传入 AlgorithmParameterSpec 算法参数规范参数.
        IvParameterSpec parameterSpec = new IvParameterSpec(vector_key.getBytes());
        //初始化 cipher。使用解密模式.
        cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
        //将 Base64 编码的内容解码成字节数组(因为加密的时候,对密文使用了 Base64编码,所以这里需要先解码)
        byte[] content = new BASE64Decoder().decodeBuffer(base64Encode);
        //执行解密操作。返回解密后的字节数组,此时可以使用 String(byte bytes[]) 转成源字符串.
        byte[] decrypted = cipher.doFinal(content);
        return new String(decrypted);
    }

    public static void main(String[] args) throws Exception {
        String content = "红网时刻5月17日讯(记者 汪衡 通讯员 颜雨彬 汪丹)“场面壮观,气势磅礴,简直就是一部抗洪抢险水域救援大片!”抗洪抢险水域救援实战训练刚一结束,现场围观的群众纷纷发出了感叹。5月16日,湖南省消防救援总队在长沙市望城区千龙湖举办舟艇操作员培训,长沙、株洲、湘潭等40余名消防指战员参加,开展了一场抗洪抢险水域救援实战训练。" +
                "夏以来,随着湖南防汛救援任务的日趋繁重。为提高全省消防救援队伍水域救援能力,充分发挥应急救援“主力军”和“国家队”的作用,湖南省消防救援总队组织了这次为期35天的培训。依托长沙、岳阳、常德3支省级水域救援队设立了3个培训点,全省653名学员分17期参加培训。此次培训采用课堂授课、实地讲解、实操训练等多种形式,培训内容贴近实战,主要包括OSO驾驶、快速出入库、翻船自救、故障排除、200米游泳、受限控艇等项目。省消防救援总队相关负责人介绍,为了加快适应“全灾种、大应急”的职能定位,全面做好“防大汛、抢大险、救大灾”的准备工作,湖南消防救援队伍未雨绸缪,组建了1支省级抗洪抢险救援队、13支市级抗洪抢险救援队,有水域救援任务的消防救援站均成立了1支站级抗洪抢险救援队,总人数2300余人。此外,省消防救援总队还把重型工程机械救援大队、航空救援大队作为抢险救援机动力量纳入调度体系,充分发挥消防救援队伍装备技术优势,最大限度地挽救生命,减少灾害损失。";
        //生成密钥的 key 与 生成算法参数规范的 key 加解密必须一致,它就像是一把钥匙或者口令,不能忘记.
        //AES 加密算法时必须是 16 个字节,DES 时必须是 8 字节.
        String slatKey = "wYDJty8o8HE6YjJS";
        String vectorKey = "SC35fdGrQozSMT5a";
        String encrypt = encrypt(content, slatKey, vectorKey);
        System.out.println("源内容:\n" + content);
        System.out.println("加密后:\n" + encrypt);
        System.out.println("解密后:\n" + decrypt(encrypt, slatKey, vectorKey));
    }
}

在线源码:src/main/java/com/wmx/thymeleafapp/utils/CipherUtils.java

RSA 非对称加密详解

1. 非对称加密,需要使用 PK(PUBLIC_KEY 公钥) 与 SK( SECRET_KEY 密钥),PK 加密时,必须用 SK 解密、反之  SK 加密时,必须用 PK 解密。

2. 安全性最高,但是速度慢,适合对少量数据加密。

3、源码中有详细解释:

public class CipherRsaUtils {

    /**
     * CIPHER_TRANSFORMS : cipher 实例化时的 加密算法/反馈模式/填充方案。ECB 表示无向量模式
     * ALGORITHM: 创建密钥时使用的算法
     * KEY_PAIR_LENGTH: 秘钥对长度。数值越大,能加密的内容就越大。
     * <p>
     * 如 KEY_PAIR_LENGTH 为 1024 时加密数据的长度不能超过 117 字节
     * 如 KEY_PAIR_LENGTH 为 2048 时加密数据的长度不能超过 245 字节
     * 依次类推
     * </p>
     */
    private static final String CIPHER_TRANSFORMS = "RSA/ECB/PKCS1Padding";
    private static final int KEY_PAIR_LENGTH = 1024;

    /**
     * 生成 RSA 密钥对:公钥(PUBLIC_KEY)、私钥:PRIVATE_KEY
     *
     * @param secureRandomSeed :SecureRandom 随机数生成器的种子,只要种子相同,则生成的公钥、私钥就是同一对.
     *                         <p>randomSeed 长度可以自定义,加/解密必须是同一个.</p>
     * @return
     */
    public static KeyPair generateRsaKeyPair(String secureRandomSeed) {
        //KeyPair 是密钥对(公钥和私钥)的简单持有者。加密、解密都需要使用.
        KeyPair keyPair = null;
        try {
            //获取生成 RSA 加密算法的公钥/私钥对 KeyPairGenerator 对象
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //获取实现指定算法(SHA1PRNG)的随机数生成器(RNG)对象.
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            //重新设定此随机对象的种子.
            secureRandom.setSeed(secureRandomSeed.getBytes());
            /**
             * initialize(int keySize, SecureRandom random):使用给定的随机源(random)初始化特定密钥大小的密钥对生成器。
             * keySize: 健的大小值,这是一个特定于算法的度量。值越大,能加密的内容就越多,否则会抛异常:javax.crypto.IllegalBlockSizeException: Data must not be longer than xxx bytes
             * 如 keySize 为 2048 时加密数据的长度不能超过 245 字节。
             */
            keyPairGenerator.initialize(KEY_PAIR_LENGTH, secureRandom);
            //genKeyPair(): 生成密钥对
            keyPair = keyPairGenerator.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return keyPair;
    }

    /**
     * 使用私钥(PrivateKey)对数据进行加密 或 解密
     *
     * @param content          :待加/解密的数据。如果待解密的数据,则是 Base64 编码后的可视字符串.
     * @param model            :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param secureRandomSeed :SecureRandom 随机数生成器的种子,只要种子相同,则生成的公钥、私钥就是同一对.
     *                         加解密必须是同一个.
     * @return :返回加/解密后的数据,如果是加密,则将字节数组使用 Base64 转为可视字符串.
     */
    public static String cipherByPrivateKey(String content, int model, String secureRandomSeed) {
        String result = "";
        try {
            //获取 RSA 密钥对
            KeyPair keyPair = generateRsaKeyPair(secureRandomSeed);
            /**getPrivate():获取密钥对的私钥。
             * getEncoded(): 返回已编码的密钥,如果密钥不支持编码,则返回 null*/
            byte[] privateEncoded = keyPair.getPrivate().getEncoded();
            /**PKCS8EncodedKeySpec(byte[] encodedKey): 使用给定的编码密钥创建新的 PKCS8EncodedKeySpec
             * PKCS8EncodedKeySpec 表示 ASN.1 号私钥的编码*/
            EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateEncoded);
            //创建 KeyFactory 对象,用于转换指定算法(RSA)的公钥/私钥。
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //从提供的密钥规范生成私钥对象
            PrivateKey keyPrivate = keyFactory.generatePrivate(encodedKeySpec);
            //实例化 Cipher 对象。
            result = encryptionDecryption(content, model, keyPrivate);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 使用公钥(PublicKey)对数据进行加密 或 解解
     *
     * @param content          :待加解密的数据
     * @param model            :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param secureRandomSeed :SecureRandom 随机数生成器的种子,只要种子相同,则生成的公钥、私钥就是同一对.
     *                         加解密必须是同一个.
     * @return :返回加密 或者 解密后的数据
     */
    public static String cipherByPublicKey(String content, int model, String secureRandomSeed) {
        String result = "";
        try {
            // 得到公钥
            KeyPair keyPair = generateRsaKeyPair(secureRandomSeed);
            EncodedKeySpec keySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey keyPublic = keyFactory.generatePublic(keySpec);
            // 数据加/解密
            result = encryptionDecryption(content, model, keyPublic);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 加密或解密
     *
     * @param content :待加解密的数据
     * @param model   :1 表示加密模式(Cipher.ENCRYPT_MODE),2 表示解密模式(Cipher.DECRYPT_MODE)
     * @param key     :公钥(PUBLIC_KEY)或 私钥(PRIVATE_KEY)的 key
     * @return
     */
    private static String encryptionDecryption(String content, int model, Key key) {
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMS);
            //init(int opMode, Key key):初始化 Cipher.
            cipher.init(model, key);
            //如果是解密模式,则需要使用 Base64 进行解码.
            byte[] contentBytes = content.getBytes();
            if (model == Cipher.DECRYPT_MODE) {
                contentBytes = new BASE64Decoder().decodeBuffer(content);
            }
            /**执行加密 或 解密操作。如果 contentBytes 内容过长,则 doFinal 可能会抛出异常.
             *javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes :数据不能超过xxx字节
             * 此时需要调大 KEY_PAIR_LENGTH 的值*/
            byte[] finalBytes = cipher.doFinal(contentBytes);
            //如果是加密,则将加密后的字节数组使用 Base64 转成可视化的字符串。否则是解密时,直接 new String 字符串.
            if (model == Cipher.ENCRYPT_MODE) {
                result = new BASE64Encoder().encode(finalBytes);
            } else if (model == Cipher.DECRYPT_MODE) {
                result = new String(finalBytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static void main(String[] args) {
        String content = "红网时刻5月17日讯(记者 汪衡 通讯员 颜雨彬 汪丹)“场面壮观,气势磅礴.";
        String secureRandomSeed = "TPCz0lDvTHybGMsHTJi3mJ7Pt48llJmRHb";

        System.out.println("被加密数据字节大小:" + content.getBytes().length + " 字节," + content.length() + " 个字符");
        System.out.println("源内容:\n" + content);
        try {
            //公钥、私钥必须分开使用,公钥解密时,必须是私钥解密,反之亦然.
            String encrypted = CipherRsaUtils.cipherByPublicKey(content, 1, secureRandomSeed);
            String decrypted = CipherRsaUtils.cipherByPrivateKey(encrypted, 2, secureRandomSeed);
            System.out.println("加密后:\n" + encrypted);
            System.out.println("解密后:\n" + decrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

在线源码:/src/main/java/com/wmx/thymeleafapp/utils/CipherRsaUtils.java

JCE 无限强度权限策略文件

1、Java 加密扩展(JCE)原生的 API 存在一定的限制,比如说加密的长度等,Oracle 官方提供的解决办法是,下载新的加密 jar 包替换本地 jdk 或者 jre 目录下原生的 jar 包。

2、这个增强版的 jar 包,官方称之为 JCE 无限强度权限策略文件(其实就是两个 jar 包):下载地址: Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE Download

3、JDK9 及更高版本附带了无限制策略文件,并在默认情况下使用,所以不需要再额外处理,对于低版本则需要更新:

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 80.01 MB

jce_policy-8.zip

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 77.3 K

UnlimitedJCEPolicyJDK7.zip

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 68.89 KB

jce_policy-6.zip

4、使用非常简单,下载压缩包后进行解压,然后将里面的两个 jar 包替换本地 jdk 或者 jre 下自带的即可: $JAVA_HOME/jre/lib/securit

原生 AES、DES、RSA 加解密

https://blog.csdn.net/wangmx1993328/article/details/83892385#原生 AES、DES、RSA 加解密

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐