加密和验签

一、加密与验签介绍

  大多数公共网络是不安全的,一切基于HTTP协议的请求/响应(Request or Response)都是可以被截获的、篡改、重放(重发)的。因此我们需要考虑以下几点内容:

  1. 防伪装攻击(案例:在公共网络环境中,第三方 有意或恶意 的调用我们的接口)
  2. 防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容 在传输过程被修改)
  3. 防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放)
  4. 防数据信息泄漏(案例:截获用户登录请求,截获到账号、密码等)

​ 区分签名与加密的概念

   ​   签名:目的是为了验证身份,检查篡改,防止重放,防止中间人假装自己对外发送假信息,不需要对数据进行加密处理,因此用私钥进行签名,用公钥进行解密验证。也有一些不可逆算法,两边生成签名之后比对结果

​      加密:目的是将明文变成密文进行传输,防止消息在传输中被别人破解,因此使用公钥加密,使用私钥解密

总结:公钥加密、私钥解密、私钥签名、公钥验签

加密算法我们整体可以分为:可逆加密和不可逆加密,可逆加密又可以分为:对称加密和非对称加密,不可逆算法不用于数据加密,常用于签名算法;可逆算法常用于消息加密算法 

比较推荐的几个加密算法有:

  • 不可逆加密:SHA256SHA384SHA512以及HMAC-SHA256HMAC-SHA384HMAC-SHA512
  • 对称加密算法:AES3DES
  • 非对称加密算法:RSA

可逆与不可逆算法,对称加密与非对称加密算法总结_柠檬不萌的技术博客_51CTO博客

面试官:说一下你常用的加密算法-阿里云开发者社区

二、签名算法也称摘要算法

  • 应用场景:检查报文正确性、验证身份,防止重放(可通过加时间戳的方式)
  • 方案:从报文文本中生成报文摘要
  • 常用:SHA或者MD5作为签名算法
  • 缺点:签名算法不是加密算法,不能用来加密

三、消息加密算法

一. 数字加密算法
数字加密算法中,通常可划分为对称加密和非对称加密。
1. 对称加密
     概念:加密和解密使用同一个密钥,所以叫做对称加密。
     常见的对称加密算法:DES,AES。

在这里插入图片描述

2. 非对称加密
      概念:加密和解密使用两个的密钥,一把作为公开的公钥(Public Key),另一把作为私钥(Private Key)。因为加密和解密使用的不是同一把密钥,所以这种算法称为非对称加密算法。
公钥和私钥成对存在,通常【公钥加密,私钥解密】;
公钥是基于私钥而存在的。通过私钥经过一系列算法是可以推导出公钥,但无法通过公钥反向推倒出私钥。
      常见的非对称加密算法:RSA,ECC。

在这里插入图片描述
3. 对称加密和非对称加密的区别
对称加密:加解密效率高。但在非安全信道中通讯时,密钥交换的安全性不能保障。
非对称加密:加解密效率低,但可以保证密钥安全性。
通常在实际的网络环境中,会将两者混合使用。两者混合使用的示例如下:

 在这里插入图片描述

四、接口验签实操

1. 实操说明

  接口加密与验签的方法有非常多,比如RSA(后期进行讲解),基于token等方式,而对于普通项目,我认为最重要的是防伪装攻击、防篡改攻击、防重放攻击。因为接下来的实操,主要围绕以下几点进行。

2. 验签实操--HMAC-摘要加密算法-不可逆-需密钥

  入参sign字段-接口调用方按相同算法进行计算得出摘要

  提供方通过如下方式计算得出摘要,比如入参摘要和计算得出的摘要比对一致验签通过。

  验签通过的前提下,校验入参传入ts,时间戳若时间差超过5s则不通过。

    /**
     * 摘要验证
     * @param vo 带摘要的请求vo
     * @return
     */
    protected void verifyDigest(BaseDigestReqVO vo) {
        Assert.notBlank(vo.getSign(), "[接口回调] 入参有误,sign不能为空");

        Assert.notNull(vo.getTs(), "[接口回调] 入参有误,ts不能为空");

        // 获取HMAC对象,该对象本身不保证线程安全,每次都需要进行实例化
        //利用hutool工具类cn.hutool.crypto.SecureUtil
        //getKey()方法为获取密钥String字符串的方法,也可以写死,调用方和提供方使用的密钥相同:cn.hutool.crypto.KeyUtil生成密钥的工具类
        HMac hMac = SecureUtil.hmacSha256(getKey());
        // sign本身不参与签名
        String beforeStr = Arrays.stream(ReflectUtil.getFields(vo.getClass(), field -> ObjectUtil.notEqual(field.getName(), "sign")))
                // 按照字段名字典排序
                .sorted(Comparator.comparing(Field::getName))
                // 取出所有value
                .map(field -> ReflectUtil.getFieldValue(vo, field).toString())
                // 用&进行拼接
                .collect(Collectors.joining("&"));
        byte[] digest = hMac.digest(beforeStr);

        Assert.isTrue(hMac.verify(digest, HexUtil.decodeHex(vo.getSign())), "[接口回调] 入参有误,签名错误");
       //防止重放攻击-判断ts在5秒内误差
        Assert.isTrue(verifyTs(vo.getTs()), "[接口回调] 入参有误,ts不在有效的范围内");
    }

3.消息加密算法--可逆-生成私钥和公钥并用私钥加密原文实操举例

//生成密钥对的方式也可以使用hutool中的工具类cn.hutool.crypto.KeyUtil生成密钥的工具类,代码会更简洁

public class RsaDemo {
    public static void main(String[] args) throws Exception {
        String input = "读你千遍";
        // 算法
        String algorithm = "RSA";

        // 生成密钥对
        KeyPair keyPair = getPrivatePublicKey(algorithm);

        // 使用私钥对原文加密
        byte[] bytes = privateKeyEncrypt(input, algorithm, keyPair.getPrivate());

        // 使用私钥对原文解密,使用私钥加密必须用公钥解密
        privateKeyDecrypt(bytes, algorithm, keyPair.getPublic());
    }

    /**
     * 使用私钥对原文加密
     *
     * @param input
     * @param algorithm
     * @param privateKey
     */
    private static byte[] privateKeyEncrypt(String input, String algorithm, PrivateKey privateKey) throws Exception {
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);
        // 加密初始化:加密模式,想使用私钥进行加密
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        // 使用私钥对原文加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println("加密:\n" + Base64.encode(bytes));
        return bytes;
    }

    /**
     * 使用私钥对原文解密
     *
     * @param input
     * @param algorithm
     * @param publicKey 使用私钥加密必须用公钥解密
     */
    private static void privateKeyDecrypt(byte[] input, String algorithm, PublicKey publicKey) throws Exception {
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);
        // 解密初始化:解密模式,想使用公钥进行解密
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 使用私钥对原文加密
        byte[] bytes = cipher.doFinal(input);
        System.out.println("解密:\n" + new String(bytes));
    }

    /**
     * 生成密钥对
     *
     * @param algorithm
     * @return
     */
    private static KeyPair getPrivatePublicKey(String algorithm) throws Exception {
        // 创建密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        byte[] privateKeyEncoded = privateKey.getEncoded();
        byte[] publicKeyEncoded = publicKey.getEncoded();

        System.out.println("私钥:\n" + Base64.encode(privateKeyEncoded));
        System.out.println("公钥:\n" + Base64.encode(publicKeyEncoded));
        return keyPair;
    }
}

输出结果

私钥:
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCOhxmGwp2dHQYCZHkxzO4EVb0lff6l/7K4p+1zZHLKBVdM7Hejm+EdFPf7CJfi44d5JHYcZSegssI8bm80sg1s38F8yfNmyZde/11aH351HFErNqYQ3KcwF8vOEg+7Ywm4PVfF0uAoLTR4KBgwXwuHUyEw80W0ADcpY8TVQe31NjJBXNK9QltoQRjrVDV8M9dJkP0QnJ2uEVU6rOyJIgMzFl+CdFmlym5AyAZFWxqDtkSbVjMsk/N5YrMTzOEhUPksEtFu4Tvv9rLe6R92287LY2b64aORc3OP40T5g+JJ4arsmG7lNjvUL/bg43Dq1uktbjMJSKWlOziIGB9Y9OpdAgMBAAECggEAX2jCxro6xjTqk+lsU9EKOC6o+pMYR0ke+4u2YXuJeAERSrY09HnhvRUPV6WN/10ukAK/agyw9VsUuV135f+XCx3SKVYLMPCb+wTZGf5hKcY188aWbk03QfN8A8uFanxGwmWV/yBF278/E3xLjP3i+sjEPD9tlqxaQMO/vL5wq6eD06RENDhmV3Di3sPPk0LMJPg4gtrWvM+TNGzHvsliADC2yHYuTSbAnTvetQJvVUizMJi2YALyFSOBnPeEiEsecTXBFwpHcU+GK60IIGanW6U3xSC7BjZu/vEpWiewVfFWSIj6ELZeCgXYpINrU6ABIheNr34oVrynONjwEcFmAQKBgQDhIY6EvOpV9iGbY00DnNgT+0TRQx2QGDIcM/KMcuwptKgGsWMBPvoRJnwQvgLLmgCPX7SQrAUk+/QvhTgXQgNoeJMb2slB17rKAdM3/qKTCBN+oZ8WbC9HrcWB5AuNWQLSaRmqIN47CdI2rN24GfpYOw5urIwINdDr7CIvdTHdgQKBgQCiEgo17wtlEdJnhFAZVC9CuIPjtQ/Ys8an9gKQDiymCPHqVgrkkzPhTSGE9fg0chkLjvi4hrNVwGHjRTyXUQFyB2o+zuZJYBQLw2i7P001hHZz/y76mTL8Oue1QhqbvJ4MpBtrV8yMBKrRxWt/BF4lY+DN2aRezkAawWMmnWWy3QKBgDqLsndcdYhDSLwTF80PtbWEi4Hr9T0qvaVN8Q6LOFUkMOoEqV2Clh9tpafo8esmsmyWk+tngLL8fqT4/Pw3Y5GAgaklvV7NDjtIPDh8lKSt3fv5Zdi765O1Yf6EYmiwtCYpxM3UXFZ4GF21mLcuskbNnNQ1NPlRnXIs5zr2PDCBAoGBAIPj9GVBvF8qqrRNK7YBGVjNuZ8UGOo8Gt1SyXEb59/ShbQzAzhSRrUBxNZkKPHdLF6IogXgsI6HOeHu1Uk6xddbC25Dh4qM4qNUCaXf9OAphRAOyddr1t8GvMt4GRlF3MTiw+GOGMqVfcGqTDmYf1kkN6ytgiMY63gaiqrBCiMNAoGBAJzoOmipXLXrpH6WS/8NN2cHOIpfK/Nco84XUpTQaihkW3nGsypVRCLmtJgsa55Efa0/T9wVqgPfYf++IHVBidvH4+KrPBetqWF0Q8km0K00DV9OXUnhRDabM4+jlaxnf4VaxWTJDNLf2cAsHwzlVMZkwT/4EsxwRggjUZTMsn6N
公钥:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjocZhsKdnR0GAmR5MczuBFW9JX3+pf+yuKftc2RyygVXTOx3o5vhHRT3+wiX4uOHeSR2HGUnoLLCPG5vNLINbN/BfMnzZsmXXv9dWh9+dRxRKzamENynMBfLzhIPu2MJuD1XxdLgKC00eCgYMF8Lh1MhMPNFtAA3KWPE1UHt9TYyQVzSvUJbaEEY61Q1fDPXSZD9EJydrhFVOqzsiSIDMxZfgnRZpcpuQMgGRVsag7ZEm1YzLJPzeWKzE8zhIVD5LBLRbuE77/ay3ukfdtvOy2Nm+uGjkXNzj+NE+YPiSeGq7Jhu5TY71C/24ONw6tbpLW4zCUilpTs4iBgfWPTqXQIDAQAB
加密:
a1UuipQu38r5lTtBVA3JnGPjLqHrAbMwXacPjOl3pzKl9Vkv/1D3tjAJw4dKlt/VHD1kPJmlvjQG4op+JODR/MrwyhZCB6bpVG0HMkqCrk8RBiGQYrxAN0H6fuEx2KtoV9HjroU+hIXM1C7SD6Kx5ss26Wb82UXsSmuIx+aGr8794fsamK9SxGTcyKYiPRrKK+PnJHDif1+2FHKrPVmWFJPFR8gUSGXYsyGTyBZarFK1bXgKGBJBF6nZ6ju3uc4g7KWbxQLsj3U2Lzz2m3U+SHEqobtWi0ssoEWdAQJUWfKdLBcvghKEyGbzQyHUfyY7sW4HIbcCYFDEA4buYZqWgw==
解密:
读你千遍

4.数字签名-是非对称密钥加密技术与摘要算法的结合应用。

 

张三有俩好哥们A、B。由于工作原因,张三和 AB 写邮件的时候为了安全都需要加密。于是张三想到了数字签名:

  1. 加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。
  2. A或者B写邮件给张三:A 先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。
  3. 张三写邮件给 A 或者 B:
    • 张三写完邮件,先用 hash 函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。
    • A 或者是 B 收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值