系统学习Hash算法对称加密非对称加密公钥私钥签名证书国密算法(附JAVA案例)

前言

日常工作开发的过程中,我们经常会遇到一些涉及密码的场景,网上的答案千篇一律,常常让人眼花缭乱,对有选择恐惧症的人来说非常容易迷失在知识的海洋里无法自拔,对写代码有强迫症的人来说简直是毁灭性打击。网络上铺天盖地的碎片化信息,非常不容易构建自己的知识体系。下面就根据我的一些理解梳理一下这部分,内容较长耐心读完,建议收藏

一、Hash算法

Hash算法也被称为散列算法,Hash算法虽然被称为算法,但实际上它更像是一种思想。Hash算法没有一个固定的公式,只要符合散列思想的算法都可以被称为是Hash算法。

1、Hash算法的特点
  • 任意长度的信息与一串固定长度的字符串建立对应关系,即哈希值定长;
  • 哈希值算法将任意长度映射为有限长度,难免会发生碰撞(两个不同信息算出的摘要相同),好的哈希算法能够尽量减少碰撞的几率;
  • 原始信息的任何一点修改都会导致计算出的哈希值的变化,从而可以用哈希值来确保消息体的完整性;
  • 哈希值算法是不可逆的,即只能从原信息计算出哈希值,不能由哈希值计算得到原信息;(网上的哈希解密,并不是反向推理,而是通过撞库的方式,即提前生成大量字符串对应的哈希值存库,然后根据用户输入的哈希值区比对。)
2、常用的Hash算法

MD5(128bits)SHA-1(160bits)RepeMD-160(160bits)SHA-256(256bits)SHA-512(512bits)

3、HASH算法用途

校验下载文件

我们只需要计算本地文件的哈希值,再与下载文件公开的哈希值比对,如果相同,说明文件下载正确,否则说明文件已经被篡改。因为相同的输入永远会得到相同的输出,如果输入被修改了,得到的输出就会不同。

比特币挖矿

比特币挖矿是通过不断修改区块头部的随机数,再去计算区块头部的哈希值,直到这个哈希值满足了一个特定的标准,然后通告全网的过程。

网站密码保护消息摘要签名

在密码学中,哈希算法的主要作用是用于消息摘要和签名,主要用于消息的完整性校验。大部分网站对用户密码保护也是利用哈希值不可逆这个特点。数据库只保存用户密码的哈希值,进行登录操作时,将此次输入的密码再次计算哈希值与数据库保存的哈希值对比,验证通过则认为密码正确。这样即使数据库泄露或消息被盗取,黑客也无法获知用户的真实密码。

4、代码示例(这里列举几个换汤不换药
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author: Marlon
 * @date: 2022/9/4 18:06
 **/
public class Test {

    public static void main(String[] args) throws NoSuchAlgorithmException {
        //MD5
        String password = "This Password";
        //输出密文
        System.out.println("MD5:" + MD5Handle(password, null, AlgorithmEnum.ALGORITHM_MD5.getAlgorithm()));
        /**
         * 这样一来我们的密码就被加密处理了,可以使用密文在数据库中进行存储,但只这样存储,也会遭到黑客手中彩虹表的入侵,
         * 于是我们将采用特殊措施来抵御彩虹表攻击:对每个口令额外添加随机数,这个方法我们称为加"盐":
         */
        String salt = "salt";
        //加盐输出
        System.out.println("MD5加盐:" + MD5Handle(password, salt, AlgorithmEnum.ALGORITHM_MD5.getAlgorithm()));

        //SHA-1
        String password1 = "This Password";
        System.out.println("SHA-1:" + MD5Handle(password1, null, AlgorithmEnum.ALGORITHM_SHA_1.getAlgorithm()));
        String salt1 = "salt";
        System.out.println("SHA-1加盐:" + MD5Handle(password1, salt, AlgorithmEnum.ALGORITHM_SHA_1.getAlgorithm()));

        //SHA-256
        String password2 = "This Password";
        System.out.println("SHA-256:" + MD5Handle(password2, null, AlgorithmEnum.ALGORITHM_SHA_256.getAlgorithm()));
        String salt2 = "salt";
        System.out.println("SHA-256加盐:" + MD5Handle(password2, salt, AlgorithmEnum.ALGORITHM_SHA_256.getAlgorithm()));
    }

    private static String MD5Handle(String password, final String salt, final String algorithm) throws NoSuchAlgorithmException {
        //根据当前算法,获取加密工具对象(摘要)
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        if (StringUtils.isNotEmpty(salt)) {
            password = password + salt;
        }
        //更新原始数据
        messageDigest.update(password.getBytes());
        //获取加密后的内容
        byte[] resultByteArray = messageDigest.digest();
        //加密后的字节数组,转换为字符串
        StringBuilder message = new StringBuilder();
        for (byte b : resultByteArray) {
            //字符串格式化控制:转换成16进制,每个数字占两个字节,不足0来补
            message.append(String.format("%02x", b));
        }
        return message.toString();
    }

    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    public enum AlgorithmEnum {
        ALGORITHM_MD5("MD5"),

        ALGORITHM_SHA_1("SHA-1"),

        ALGORITHM_REPEMD_160("RepeMD-160"),

        ALGORITHM_SHA_256("SHA-256"),

        ALGORITHM_SHA_512("SHA-512");

        private String algorithm;
    }
}

/**
	* 输出结果:
	* 
	* 	MD5:1145e06a8c61b6d5cbfadc628b98a918
	*	MD5加盐:542e19ef8927567278fb6a73cfb2f0a7
	*	SHA-1:05b235d2e02ae8f7dabef992918f91a2e3a63875
	*	SHA-1加盐:d727175ec0257429cb39caac5f1e90cab7b810f3
	*	SHA-256:2b1cff5ca16c53fc0a18bcaa3d681d776d2fbe939eb1831d5bc11fbaf05158e6
	*	SHA-256加盐:0f9cc83783003baf11656355938fc244fb2e3b5e911e345781ed89b9bbcf6389
	*
	**/


二、对称加密

对称加密采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
在这里插入图片描述
专业回答:

甲方(主导方或者服务器方)生成加密密钥,将密钥私下共享给乙方(客户方或者受众方),当甲方或者乙方用密文信息交换时,均用此钥将明文加密生成密文或者密文解密生成明文。

业余回答:

上个世纪电报加密技术,就是初代网络安全所使用的加密方式。通过发信时根据密码本将内容翻译为密文发出,收到电报的一方,使用相同的密码本解密出正确的信息,这样的加密方式原理与对称加密一样。

常见的对称加密算法:

AES(比较常用)DES、、3DESDESXBlowfishIDEARC4RC5RC6

优点:

  • 生成密钥的算法公开;
  • 计算量小;
  • 加密速度快、加密效率高;
  • 密钥较短;

缺点:

  • 密钥需要传递,难以确保密钥安全性;
  • 不能作为身份验证,密钥发放困难,安全性得不到保证;
代码示例(这里列举常用的AES
package com.example;

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * @author: Marlon
 * @date: 2022/9/13 22:11
 **/
public class AESUtil {


    /**
     * 密钥算法
     */
    private static final String KEY_ALGORITHM = "AES";

    /**
     * 加密/解密算法-工作模式-填充模
     * 默认的加密算法
     */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    /**
     * AES 加密操作
     *
     * @param key      密钥
     * @param password 加密密码
     * @return 返回Base64转码后的加密数据
     */
    public static String encrypt(final String password, final String key) {
        try {
            //根据指定的加密算法 创建密码器
            final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            //获取要加密内容的字节
            final byte[] byteContent = password.getBytes(StandardCharsets.UTF_8);
            //利用加密密钥将密码器对象初始化:参数一:加密/解密模式,参数二:加密后的钥匙(密钥)
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(), KEY_ALGORITHM));
            //加密
            final byte[] result = cipher.doFinal(byteContent);
            //防止乱码,使用Base64编码
            return Base64.encodeBase64String(result);
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * AES 解密操作
     *
     * @param key      密钥
     * @param password 解密密码
     * @return 明文
     */
    public static String decrypt(String password, String key) {
        try {
            //创建密码器
            final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(), KEY_ALGORITHM));
            //执行操作
            final byte[] result = cipher.doFinal(Base64.decodeBase64(password));
            return new String(result, StandardCharsets.UTF_8);
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 获取AES秘钥
     *
     * @return
     */
    public static String getKey() {
        try {
            final KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
            kg.init(128);
            final SecretKey sk = kg.generateKey();
            final byte[] b = sk.getEncoded();
            return byteToHexString(b);
        } catch (NoSuchAlgorithmException ignored) {
        }
        return null;
    }

    /**
     * byte数组转化为16进制字符串
     *
     * @param bytes
     * @return
     */
    private static String byteToHexString(final byte[] bytes) {
        final StringBuilder sb = new StringBuilder();
        for (final byte aByte : bytes) {
            final String strHex = Integer.toHexString(aByte);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0").append(strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String key = getKey();
        String encryptPassword = encrypt("password", key);
        String decryptPassword = decrypt(encryptPassword, key);
        System.out.println("密钥:" + key);
        System.out.println("AES加密:" + encryptPassword);
        System.out.println("AES解密:" + decryptPassword);
    }
}



  /**
	* 输出结果:
	* 
	* 	密钥:9824f51aba0a1d8b997bcbb2d9a5502c
	*	AES加密:06qTFE6arRP1/k7jG/eezQ==
	*	AES解密:password
	*
	**/


三、非对称加密

非对称加密算法需要一对密钥(两个密钥):公开密钥(publickey)和私有密钥(privatekey)(简称公钥,私钥)。公开密钥与私有密钥生成时是一对,用公钥加密只能是对应的私钥解密,同理用私钥加密只能用对应的公钥解密。
在这里插入图片描述

专业回答:

甲方(主导方或者服务器方)生成一对密钥(也就是公钥和私钥),并将其中的一把公用密钥向其它方公开或者私下共享。得到该公用密钥的乙方,使用该公钥对传输信息进行加密后再发送给甲方。甲方再用自己保存的私钥对乙方加密后发送来的信息进行解密。

业余回答:

简单来说就是有一把琐,有两把钥匙,A钥匙(公钥)和 B钥匙(私钥),用A钥匙锁上这把锁,那么只有用B钥匙可以打开,用B钥匙锁上这把锁,只有A钥匙可以打开。

常见的非对称加密算法:

RSA(比较常用)ECCDiffie-HellmanEl GamalDSA

优点:

  • 安全高(几乎很难破解);

缺点:

  • 加解密相对速度慢;
  • 密钥长;
  • 计算量大、效率低;

使用非对称加密进行通信

单向加密:

生成了非对称加密的公私钥对,这样通信中仅需传递公钥。需要发消息就一边使用公钥加密发送,另一边使用私钥解密,这样即使信息被获取也不可能获知信息的内容。

双向加密:

A 根据非对称加密算法生成自己的公私钥对(PUBLIC_A,PRIVATE_A);

B 根据非对称加密算法生成自己的公私钥对(PUBLIC_B,PRIVATE_B);

A 和 B 可以公开的交换自己的公钥(私钥不需要发送,各自保存好即可);

A 要发送消息给 B 时,使用 B 的公钥 PUBLIC_B 加密信息,发送给 B ;

B 接收到消息后,使用自己保存的私钥 解密就可以看到消息内容(这条消息即使被他人获得后也是不能解密的);

B 要发消息给 A 时,使用 A 的公钥 PUBLIC_A 加密信息,发送给 A ;

A 接收到消息后,使用自己保存的私钥 解密就可以看到消息内容(这条消息即使被他人获得后也是不能解密的);

这样就实现了双向加密;

签名

有了公私钥对,似乎解决了加密通信的难题。但是实际使用中又会出现新的问题了,那就是 A 收到消息后如何确认发信人是 B 而不是第三方呢?其实也很简单,只要发消息前多进行一次使用自己的私钥加密的过程就可以了,而使用自己私钥加密信息的步骤就叫做签名。

私钥只有自己持有,公钥和私钥存在一 一对应关系,用公钥只能解密出对应私钥加密的信息,因此就可以用私钥的加密过程当做验证身份的手段。其实公钥、私钥加密数据的方法与原理都相同,只是按照用途分别命名而已。

公钥一般用来加密,私钥用来签名

使用签名:

A 先使用自己的私钥 PRIVATE_A 对消息进行一遍加密(习惯性称作签名),再使用 B 的公钥 PUBLIC_B 加密信息,然后将加密结果发送给 B;

B 接收到消息后,使用自己保存的私钥 PRIVATE_B 解密,然后使用 A 的公钥 PUBLIC_A 再解密一遍,如果能解密成功,就可以确保这条消息不是伪造的;

B 要发消息给 A 时先使用自己的私钥 PRIVATE_B 对消息进行一遍加密(习惯性称作签名),再使用 A 的公钥 PUBLIC_A 加密后发出;

A 收到消息后使用自己的私钥 PRIVATE_A 解密,然后使用 B 的公钥 PUBLIC_B 再解密一遍,这样就实现了双方互相确认身份的加密通信;

由于非对称加密是复杂且耗时的,而且需要加密的内容越长就越耗时。在实际使用中一般经过摘要算法得到一串哈希值,然后使用私钥对哈希值进行加密。习惯性将这样对摘要使用私钥加密生成的文件叫做签名文件。

公钥与证书

从上边的流程来看,似乎一切都非常完美,滴水不漏,但黑客也是生尽千方百计的要搞事情,于是就把客户端连接外界的网络全部拦截,黑客把公钥A换成了自己的公钥A1,然后也生成一个私钥B1;

黑客将A1发送给客户端,客户端收到A1后,把自己的私钥加密发出去。然后就被黑客拿到了。拿到之后再通过自己的私钥B1解密拿到客户端的私钥。然后就可以借助公钥A给服务器发数据了。

不过正义终将战胜黑暗,于是CA机构应运而生;

如果用户想得到一份属于自己的证书,他应先向 CA 提出申请。在 CA 判明申请者的身份后,便为他分配一个公钥,并且 CA 将该公钥与申请者的身份信息绑在一起,并为之签字后,便形成证书发给申请者。

如果一个用户想鉴别另一个证书的真伪,他就用 CA 的公钥对那个证书上的签字进行验证,一旦验证通过,该证书就被认为是有效的。证书实际是由证书签证机关(CA)签发的对用户的公钥的认证。

这样你从这个机构获取证书,就可以得到有权威机构背书的公钥与 A 的对应关系。

证书授权中心 CA

CA 证书授权(CertificateAuthority)中心是数字证书发行的唯一机构。

CA 中心又称 CA 机构,即证书授权中心(Certificate Authority),或称证书授权机构,作为电子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。

CA 中心为每个使用公开密钥的用户发放一个数字证书,数字证书的作用是证明证书中列出的用户合法拥有证书中列出的公开密钥。CA 机构的数字签名使得攻击者不能伪造和篡改证书。

在 SET 交易中,CA 不仅对持卡人、商户发放证书,还要对获款的银行、网关发放证书。它负责产生、分配并管理所有参与网上交易的个体所需的数字证书,因此是安全电子交易的核心环节。

CA 证书是逐级保证安全的,最终的根证书内置在操作系统中。由操作系统来保证。

代码示例(这里列举常用的RSA
package com.example;

import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
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.HashMap;
import java.util.Map;
import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

/**
 * @author: Marlon
 * @date: 2022/9/13 23:23
 **/
public class RSAUtil {

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";

    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * RSA加密明文最大长度
     */
    private static final int MAX_ENCRYPT_LENGTH = 1024;

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 生成密钥对(公钥和私钥)
     */
    public static Map<String, Object> genKeyPair() {
        final Map<String, Object> keyMap = new HashMap<>(2);
        try {
            final KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            //RSA加密明文最大长度
            keyPairGen.initialize(MAX_ENCRYPT_LENGTH);
            final KeyPair keyPair = keyPairGen.generateKeyPair();
            final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            final RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            keyMap.put(PUBLIC_KEY, publicKey);
            keyMap.put(PRIVATE_KEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            System.out.println("生成公钥和私钥失败...");
        }
        return keyMap;
    }

    /**
     * 公钥加密
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     * @return 加密密文
     */
    public static String encryptByPublicKey(String data, String publicKey) throws Exception {
        byte[] encryptData = Base64.decodeBase64(data);
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        /*
            简单实现返回值为String
        byte[] res = cipher.doFinal(encryptData);
        return Base64.encodeBase64String(res);
        */
        int inputLen = encryptData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(encryptData, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return Base64.encodeBase64String(encryptedData);
    }

    /**
     * 私钥解密
     *
     * @param data       已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return 明文
     */
    public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
        byte[] decryptData = Base64.decodeBase64(data);
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        /*
            简单实现返回值为String
        byte[] res = cipher.doFinal(encryptedData);
        return Base64.encodeBase64String(res);*/

        int inputLen = decryptData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0; //偏移量
        byte[] cache;
        int i = 0;

        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(decryptData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(decryptData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return Base64.encodeBase64String(decryptedData);
    }

    /**
     * 私钥加密
     *
     * @param data       源数据
     * @param privateKey 私钥(BASE64编码)
     * @return 加密密文
     */
    public static String encryptByPrivateKey(String data, String privateKey) throws Exception {
        byte[] encryptData = Base64.decodeBase64(data);
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = encryptData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(encryptData, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return Base64.encodeBase64String(encryptedData);
    }

    /**
     * 公钥解密
     *
     * @param data      已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return 明文
     */
    public static String decryptByPublicKey(String data, String publicKey) throws Exception {
        byte[] decryptData = Base64.decodeBase64(data);
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = decryptData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;

        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(decryptData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(decryptData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return Base64.encodeBase64String(decryptedData);
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return 签名
     */
    public static String sign(String data, final String privateKey) throws Exception {
        byte[] signData = Base64.decodeBase64(data);
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(signData);
        return Base64.encodeBase64String(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param data      已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign      数字签名
     * @return boolean
     */
    public static boolean verify(String data, String publicKey, String sign) throws Exception {
        byte[] verifyData = Base64.decodeBase64(data);
        Signature signature = getSignatureByDataAndPublicKey(verifyData, publicKey);
        return signature.verify(Base64.decodeBase64(sign));
    }

    public static Signature getSignatureByDataAndPublicKey(byte[] data, String publicKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature;
    }

    /**
     * 获取私钥
     *
     * @param keyMap 密钥对
     * @return 私钥
     */
    public static String getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * 获取公钥
     *
     * @param keyMap 密钥对
     * @return 公钥
     */
    public static String getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> merKeyMap = RSAUtil.genKeyPair();
        String merPublicKey = RSAUtil.getPublicKey(merKeyMap);
        String merPrivateKey = RSAUtil.getPrivateKey(merKeyMap);
        System.out.println("公钥: " + new String(merPublicKey.getBytes()));
        System.out.println("私钥: " + new String(merPrivateKey.getBytes()));
        String encryptByPublic = RSAUtil.encryptByPublicKey("This is a cipher", merPublicKey);
        String decryptByPrivate = RSAUtil.decryptByPrivateKey(encryptByPublic, merPrivateKey);
        System.out.println("公钥加密:" + encryptByPublic);
        System.out.println("私钥解密:" + decryptByPrivate);
        String encryptByPrivate = RSAUtil.encryptByPrivateKey("This is a cipher", merPrivateKey);
        String decryptByPublic = RSAUtil.decryptByPublicKey(encryptByPrivate, merPublicKey);
        System.out.println("私钥加密:" + encryptByPublic);
        System.out.println("公钥解密:" + decryptByPublic);
        //这里只演示单方向的加密 实现数字签名
        String signature = RSAUtil.sign(encryptByPrivate, merPrivateKey);
        boolean verify = RSAUtil.verify(encryptByPrivate, merPublicKey, signature);
        System.out.println("私钥对加密信息生成数字签名:" + signature);
        System.out.println("校验结果:" + verify);
    }

}


/**
	* 输出结果:
	* 
	* 	公钥: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCP4V98e2/xvaIXbeq3cTEjKX1XbHQV0rS64vME1MmMmr5lJfKRRZVGjufA2Shb5eAlHNp+/L6Zt4bXPKQQSM4Neaj7HTqmFKKkskSvqEwL6/Qdc0MJvvCZRV8s3RJoMQ3deQY2I3UL283AXbSVHLzCzWVKFjLp44yK/aFAdvAHywIDAQAB
	*	私钥: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI/hX3x7b/G9ohdt6rdxMSMpfVdsdBXStLri8wTUyYyavmUl8pFFlUaO58DZKFvl4CUc2n78vpm3htc8pBBIzg15qPsdOqYUoqSyRK+oTAvr9B1zQwm+8JlFXyzdEmgxDd15BjYjdQvbzcBdtJUcvMLNZUoWMunjjIr9oUB28AfLAgMBAAECgYATN/V7OTnAvqXSH1qE2lfH7JjTfqca6yQ8+mefs5SgdWMNO75k7Th9TkVimx8q90+zZWFCKRyEh+E5Ct6XVkI/ksn1IHwekc811oopx3JWBUxZRc0Z9K1QE/UBDg+QEJ9TdC8hwwCMVf1CQZPz8QEOxYkdatBDxesyB5oKnZthIQJBANwR+awPQ995d+HGLSmN359T1101QLcakDHZEXtcg8YrJlSBx0wBBahQNjOTlnXKpUYaVLYk614Tn8snSDH2fckCQQCnXvoeQZP7jppPeHnoqRMaFP+LuEiZMvM5KH9ORktZpkuoJ9lI9+7ZUH9C6+bvysKBJ6mizFqiR0OqX7PZapLzAkAkrPI5FIoxMiMig05/gF9mRCi+JePVIB4r0IszTeXAHuzSZJZ8OidhN4zw54XJt633kufPOzPmh8q3pqJHgVbRAkB/QAvEIzHCqXoS/9MYHCwEwYBFF1hC+eTISUz5EyLIx1FwSNO/0ZkMKlKHa9nJ6IuXLLOhreMezDXa/CY+H/QXAkEAlcjebqjkDrjP93mH1zJF0NJlGTYhpO2V/L+92Sy9aguiJgEliKfHW88mxqmsPDoUcSS2TiLF4r3u2p0W/G0jJQ==
	*	公钥加密:dWsgMB0ICfsRp5KpWapiu27tQT5EnJYrDxR6CAkLNfF8inM4BsqTLO5A75XrHb1KhnIlp06QavXFfOifLQ6IdL53Z2W7U/uG1IoxRAXMUKANlMSQgqarIjDWaV60pEOUHcMpmXowIAOIFo9sHWBePQP468vjSvH0c84+dIi9QAg=
	*	私钥解密:Thisisaciphe
	*	私钥加密:dWsgMB0ICfsRp5KpWapiu27tQT5EnJYrDxR6CAkLNfF8inM4BsqTLO5A75XrHb1KhnIlp06QavXFfOifLQ6IdL53Z2W7U/uG1IoxRAXMUKANlMSQgqarIjDWaV60pEOUHcMpmXowIAOIFo9sHWBePQP468vjSvH0c84+dIi9QAg=
	*	公钥解密:Thisisaciphe
	*	私钥对加密信息生成数字签名:E74dNJsjyz23ZU9u2m5coJu7/+akohfX8bnvzDmYzhThBC9FnX26RAMwRwUHYolfXFo6V+h+Z7R2PH9dzHg62cgNP7oOsjqyVMo78NaE6ftgDESvwyrAQyuvLPby+uv/7j9YGz8DrD1chaYyjz131mjsB443hUiGAyc83XohxXQ=
	*	校验结果:true
	*
	**/

五、国密算法

国密算法是我国自主研发创新的一套数据加密处理系列算法。从SM1-SM4分别实现了对称、非对称、摘要Hash等算法功能。特别适合应用于嵌入式物联网等相关领域,完成身份认证和数据加解密等功能。当然前提条件是算法密钥必须保证安全性,因此要将国密算法嵌入到硬件加密芯片中结合使用。

SM1:

国密SM1 为对称加密算法中的分组加密算法,其分组长度、秘钥长度都是128bit。该算法的实现原理没有公开,他的加密强度和 AES 相当,需要调用加密芯片的接口进行使用。

采用该算法已经研制了系列芯片、智能IC卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。

SM2:

国密 SM2 为非对称加密,也称为公钥密码;它是基于椭圆曲线密码的公钥密码算法标准。

它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,其安全强度比RSA 2048位高,且运算速度快于RSA。包含数字签名、密钥交换和公钥加密,用于替换RSA/DH/ECDSA/ECDH等国际算法。可以满足电子认证服务系统等应用需求,由国家密码管理局于2010年12月17号发布。

SM2采用的是ECC 256位的一种,其安全强度比RSA 2048位高,且运算速度快于RSA。

SM3:

国密 SM3对标MD5/SHA-1/SHA-2等国际算法,适用于数字签名和验证、消息认证码的生成与验证以及随机数的生成,其算法公开,可以满足电子认证服务系统等应用需求,于2010年12月17日发布。

它是在SHA-256基础上改进实现的一种算法,采用Merkle-Damgard结构,消息分组长度为512bit,输出的摘要值长度为256bit。

SM4:

国密SM4 是一种 Feistel 结构的分组密码算法,其分组长度和密钥长度均为128bit。加解密算法与密钥扩张算法都采用 32 轮非线性迭代结构。解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,即解密算法使用的轮密钥是加密算法使用的轮密钥的逆序。

国密 SM4跟SM1类似,是我国自主设计的分组对称密码算法,用于替代DES/AES等国际算法。SM4算法与AES算法具有相同的密钥长度、分组长度,都是128bit。于2012年3月21日发布,适用于密码应用中使用分组密码的需求。

SM7:

国密SM7也是一种分组加密算法,该算法没有公开。SM7适用于非接IC卡应用包括身份识别类应用(门禁卡、工作证、参赛证),票务类应用(大型赛事门票、展会门票),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通、公交一卡通)。

SM9:

国密SM9是我国采用的一种标识密码标准,用椭圆曲线对实现的基于标识的数字签名算法、密钥交换协议、密钥封装机制和公钥加密与解密算法,包括数字签名生成算法和验证算法,并给出了数字签名与验证算法及其相应的流程。并提供了相应的流程。可以替代基于数字证书的PKI/CA体系。

由国家密码管理局于2016 年 3 月 28 日发布,相关标准为“GM/T 0044-2016 SM9标识密码算法”。在商用密码体系中,SM9 主要用于用户的身份认证。SM9 算法不需要申请数字证书,适用于互联网应用的各种新兴应用的安全保障。据新华网公开报道,SM9 的加密强度等同于 3072 位密钥的 RSA 加密算法。
在这里插入图片描述

文末分享:

  • 互联网时代,每天铺天盖地的碎片化信息,你拼命追赶,生怕错过一条就好像要被时代所抛弃一样,而殊不知这些碎片化的信息,正在一步步毁掉你深度思考的能力。

文章仅用作记录分享,若有不当,还望指正。VX: MYY668999程序猿爱篮球

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿爱篮球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值