aes加密和sm2非对称加密

1,628 阅读6分钟

一、前言

之前我们试了md5和des加密,第一个是直接转义,第二个是密钥长度太短,这两种都可能被暴力破解,通过穷举法算出明文,所以今天我们来试试新的加密方式aes和sm2,这两种的安全性更高,更有保障。

二、Aes

1、简介

aes全称(Advanced Encryption Standard),又叫Rijndael加密法,是高级加密标准。

它也是对称加密的方法,但这里用的密钥长度为128bit、192bit、256bit其中之一(字节就是16字节、24字节、32字节),都远远大于des加密的的56bit密钥,安全性有了极大的提升。

2、加解密过程

加密: 明文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)

解密: 密文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)

3、优势和应用

对称加密的算法,加密速度都很快,适合应用在大量数据传输加密的场景,也可以用来加密一些非敏感数据。

4、风险

第一个是暴力破解,这个风险性比较小,aes的密钥长度可以劝退大部分情况了。

第二个是密钥泄露,由于是对称加密,即加解密共用一个密钥,所以前端是存有一份的,这份密钥明文存储在前端项目中,懂前端的人用心找找,还是能找到该密钥的。有密钥之后,解密将变得简单起来。(所以如果要更安全,可以选用非对称方法加密,也可以对aes的密钥进行加密)

5、使用方法

5.1 安装

安装crypto-js,已经集成了,就不额外安装了。

image.png

import CryptoJS from 'crypto-js' 

5.2 科普

由于aes是分组加密算法,区块长度固定128位,所以我们选用pkcs7填充方式,这里科普一下pkcs7和pkcs5。

pkcs7填充方式: 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小;PKCS5只填充到8字节,而PKCS7可以在1-255之间任意填充。

pkcs5填充方式: PKCS#5填充是将数据填充到8的倍数,填充后数据长度的计算公式是 定于[元数据]长度为x, 填充后的长度是 x + (8 - (x % 8)), 填充的数据是 8 - (x % 8),块大小固定为8字节。

pkcs5的固定8字节填充,已经劝退了aes了(aes最小字节数为16字节)。

5.3 具体函数封装

下面是加解密代码函数的写法,和des基本相同,只需要传入明文或者密文,调用之后对应函数后,就能返回加密后的密文或者解密后的明文字符串。

  const CBCIV = 'sajajg' // 密钥串
// 加密
  encrypt(data) {
    const key = CryptoJS.enc.Utf8.parse(CBCIV)
    const secretData = CryptoJS.enc.Utf8.parse(data)
    const encrypted = CryptoJS.AES.encrypt(
      secretData,
      key,
      { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7
      }
    )
    return encrypted.toString()
  },

  // 解密
  decrypt(data) {
    const key = CryptoJS.enc.Utf8.parse(CBCIV)
    const decrypt = CryptoJS.AES.decrypt(
      data,
      key,
      {
        mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7
      })
    return CryptoJS.enc.Utf8.stringify(decrypt).toString()
  }

三、 sm2非对称加密

1、简介

SM2是[国家密码管理局]于2010年12月17日发布的椭圆曲线公钥密码算法。

目前RSA加密算法已经不再安全,RSA是基于‘大数分解’这一数学难题设计的、确定性的一种加密算法。主要是靠指数难分解,运算量大来确保安全性。

如果‘大数分解’这一数学难题被攻克(这个比较难说),就是运算量变简单了,那也会导致RSA加密变得不再具备安全性。

另一个的运算量大,对目前的计算机性能来说,已经不再是难事了,1024位RSA已经可以在短时间内被破译出来明文,2048位RSA暂时还没被攻克,但估计也不远了。

所以目前是用密码复杂度高、处理速度快、机器性能消耗更小的SM2加密算法来替代RSA算法。

2、加解密过程

加密

每次加密都会经过三种加密算法,c1、c2、c3分别计算得出结果,最后将这三个结果依次异或处理,得到加密的密文。

c1: 随机数K与G(x,y)的多倍点运算结果,结果也是一个点,记录为(kx,ky)。
c2: 实际密文值。
c3:使用SM3对于 kx||data||ky的hash值,在解密时校验解密结果是否正确。

整体流程图如下: image.png

解密

首先是密钥流的计算

1. 从密文中分离出 c1(x,y)
2. 使用私钥d与c1进行多倍点运算,得到计算结果 (cx,cy),d·c1(cx,cy)
3. 根据c2长度计算密钥流,KDF(cipherlen,cx,cy)

其次是计算出原文

1. 原文 = 密钥流 (异或) 密文

最后是校验准确性

1. 基于以上的运算结果的cx,cy与得到的原文
2. SM3(cx,计算得到的明文,cy)
3. 对比计算结果与c1,一致解密成功,不一致则解密失败

整体流程图如下: image.png

3、优势和应用

安全系数更高

sm2采用椭圆曲线密码理论基础设计得出,加密强度比2048位RSA还高,同时可扩展性强于RSA,破解难度也比RSA要大得多。

性能表现更好

sm2的密钥长度仅256位,计算过程对计算机各方面的需求不高,占用低,效率高。

下面有个对比图,可以看看。 image.png

4、风险

除了密钥泄露,目前暂时没有其他风险。

5、使用方法

5.1 安装

这次不在crypto-js里了,我们需要额外安装

npm install --save sm-crypto

5.2 具体函数封装

一般我们都是新加一个js文件(路径/utils/sm2.js),将加密方法封装起来,在模块里按需引用

const sm2 = require('sm-crypto').sm2 // 获取sm2对象

定义加密策略和公钥a,私钥b,这是两对密钥对,剩下的后端持有私钥a和公钥b,所以就算前端这边泄露了公钥a,私钥b,两个密钥也不匹配,篡改不了信息。

同时这里的密钥对,必须是符合规范的,不能是以下我提供的样例,否则会报错

// 加密策略为0时在加密的串前加上04,后端才能解密成功,
// 同样后端加密后也会返回带04的加密串给前端,
// cipherMode为1的话必须去掉04才能解密成功
const cipherMode = 0 // 选择加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const sysPublicKey = 'asfaa' // 系统后台公钥
const uiPrivateKey = 'sjsjfb' // 前端UI私钥

封装具体函数,主要是否要+04

export default {
  // 加密
  sm2DoEncrypt(data) {
    if (cipherMode === 0) {
      // 加'04'
      return '04' + sm2.doEncrypt(data, sysPublicKey, cipherMode)
    } else {
      return sm2.doEncrypt(data, sysPublicKey, cipherMode)
    }
  },
  // 解密
  sm2DoDecrypt(data) {
    let dataHex
    if (cipherMode === 0) {
      data += '04'
      dataHex = data.substring(2).toLocaleLowerCase()
    }
    return sm2.doDecrypt(dataHex, uiPrivateKey, cipherMode)
  }
}

最后在模块里使用

 import sm2 from '@/utils/sm2'
 // 加密
 const str = sm2.sm2DoEncrypt('042ssaf1231')

加密成功差不多就是这样一串字符串

image.png

四、总结

今天试了两种加密算法aes和国密sm2,如果是安全性要求比较高的系统,建议还是选用这两种加密方式,安全性更有保障。

ps: 我是地霊殿__三無

Snipaste_2022-07-19_15-30-26.jpg