65.9K
CodeProject 正在变化。 阅读更多。
Home

创建认证加密并使用 AES256 加密您的数据

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2022 年 6 月 30 日

CPOL

10分钟阅读

viewsIcon

9154

downloadIcon

191

提供完整可用的示例代码和对创建 AES256 认证加密所需内容的所有解释。

引言

本文的灵感来自于一个简单的事情:希望创建一个允许轻松、安全地存储数据的 WebAPI。

在线试用

在继续之前,您可以尝试一下并在 AuthEncryptViaAES256 - JSFiddle - Code Playground[^] 中查看代码。

我还附带了一个 zip 文件供您下载。或者,您可以在我的 Github 仓库中获取此项目的代码:GitHub - raddevus/Aes256ViaJS: 使用 CryptoJS 库通过 AES256 加密/解密数据(包括认证加密)[^]。

本文的其余部分将解释使用代码时实际发生的情况。

关于通过 JavaScript 实现的 AES256 算法

我的所有代码都只是实现了 AES256 算法,该算法在 https://code.google.com/archive/p/crypto-js/[^] 中有更详细的文档。

注意:我如何知道该库中提供的代码有效?因为我使用 C#、Java(和其他语言)实现了解密端,这些语言有自己的 AES256 加密库,并且这些库可以解密此 JavaScript 库加密的数据。

背景

我最近在 CodeProject 上开始了一系列与此想法相关的文章(为什么是 HTML5?为什么不呢?UI/UX 与存储挑战(第 1 部分,共 N 部分)[^])。

应用程序的轻松、安全、可靠的数据存储

基本观点是,您可以使用基于 Web 的技术来创建完整的应用程序,但仍然没有简单、安全、可靠的方法来真正保存数据,并使其在用户运行您的应用程序的任何地方都可用。

我发现如何使用 AES256 加密您的数据,并使用我简单的 LibreStore WebAPI 进行存储(关于 WebAPI 的更多内容即将推出)。

本文与其他文章的区别

然而,在阅读一本名为《真实世界密码学(Manning Publishers - 通过亚马逊)[^]》时,我偶然发现了一个叫做“认证加密”的东西(本文将详细介绍)。

认证加密是我们加密数据时的真正目标。我从未见过对此的解释。相反,大多数文章只是向您展示使用 AES256 加密您的明文数据的技术方面。但是,我从未见过一篇文章解释 IV(初始化向量)、MAC(消息认证码)的所有细节,以及什么可以是明文,什么必须加密或保密。

加密的两个挑战

对称加密中的两个主要挑战必须得到解释,在我看来,这两个挑战是:

  1. 知道自己做对了——你是否正确地实现了算法?你是否为每次加密生成了 IV?
  2. 什么必须保密,什么可以明文?IV 可以公开,还是必须保密?哪些项目必须保密?

一个额外的挑战

实际上还有一个技术挑战,我将在未来的文章中提及。

每种语言的 AES256 加密和解密方式略有不同:Java、JavaScript、C# 等中的 API 不同。

重点是,您可能使用 AES256 正确加密了数据,却发现您的客户端(用 C# 编写)有一个完全不同的加密 API,您必须学习才能解密它。

让我们开始吧。

认证加密

如果您要使用 AES256 等对称加密算法,那么必须确保加密数据没有被篡改。这就是认证加密所保证的。

高层视图

可以通过以下方式实现认证加密:

  1. 生成一个随机 IV(初始化向量)——明文发送给解密方
  2. 加密您的数据
  3. 对 IV 和加密数据生成 MAC(消息认证码)(使用密钥)
  4. 将 MAC 发送给解密方,以便他们可以测试相同的结果

确切过程有点令人困惑

创建认证加密的过程有点令人困惑,所以这里是我在正确加密用户数据时学到的东西。
您可以阅读更多关于认证加密 - 维基百科[^] 的信息。

不难,但信息难以查找

这并不难做到,但很难找到所有信息集中在一个地方,所以我决定写下这篇文章。

我们需要什么来进行认证加密?

认证加密需要一些东西:

  1. 明文数据 -(要加密的数据)
  2. 密码 - 在我们的例子中,我们将使用一个密码,它将由用户提供用于加密。关于强密码有很多要说的,所以我们在这里不再深入讨论。重点是解密方也将使用这个秘密。而且,是的,密码在被使用之前就已经经过 SHA256 哈希处理,所以不用担心。
  3. 随机 IV(初始化向量),将以明文形式传递——AES256 需要 16 字节的随机数据,并且每次加密数据时都应创建此 IV,即使密码或明文数据没有改变。
  4. MAC 密钥 - 用于生成 MAC(消息认证码)的密钥,这是 16-32 字节的数据,在您和将解密数据的接收者之间保密。
  5. MAC - 最终的消息认证码 - 使用 MAC 密钥(密钥)对 IV 和加密数据生成 - 此代码将以明文形式传递给接收者(解密方)。必须知道此 MAC,以便接收者可以将其与他们计算的值进行比较,以确保 IV 和加密数据未被篡改。

您将哪些项目提供给解密方?

《真实世界密码学》中主要混淆点之一是因为不知道哪些项目应该提供给解密方。

如果您不确定,您可能会觉得您正在暴露数据,而攻击者可能会利用这些数据来窃取或破坏加密数据。

这是您必须提供给解密方的确切列表,以及他们应该以何种格式(明文或加密)提供。
注意:当我提到某个东西是明文时,意味着该元素可以暴露给任何人。例如 - 数据元素不必加密即可保护您的数据安全。

  1. 加密数据——显然,这是您不想暴露的秘密数据。注意:我们的encryptedData(加密后)将进行base64编码(加密后),以便更容易通过 HTTP 传输。
  2. IV(初始化向量)——这是一个明文值,在我们的例子中,我们使用CryptoJS库生成一个随机值。这将是 16 个随机字节(128 位),并在加密数据时用于随机化输出,使加密数据更具随机性且更安全。您提供给解密方的数据格式将是 2 字节的十六进制值,例如:137b276b3975612498e225657140c5a1
  3. MAC——这是明文消息认证码,它将由解密方用来验证您发送的 IV 和加密数据未被更改或损坏。它看起来像:57bfeffc96a32402266abe2035801e8f65cbd58fc9d88dceb591572acb1dca74

示例数据传输对象 (DTO)

如果您将所有这些数据加载到一个 DTO(JSON 对象)中,它可能看起来像这样:

{MAC:"57bfeffc96a32402266abe2035801e8f65cbd58fc9d88dceb591572acb1dca74", 
  IV:"137b276b3975612498e225657140c5a1", 
   enryptedData:"dGhpcyBpc24ndCByZWFsbHkgZW5jcnlwdGVkLCBidXQgaXMganVzdCBhbiBleGFtcGxl"
}

我们首先发送MAC(为了方便人类阅读),因为解密方将获取IV和加密数据,应用 MAC 密钥,并尝试再次生成 MAC。如果匹配,解密方就知道encryptedDataIV未被更改或损坏。

好的,最后,让我们通过一些代码来看一下。

示例代码

这其中有很多内容,所以我发布了一些示例代码,您可以通过 JSFiddle 尝试。

我们在这里关注的代码是加密代码,所以让我们看看 JavaScript(*main.js*)中的小 Encrypt() 函数。

这是整个 encrypt() 函数,因为它很简单,所以应该完全可以理解。

function encrypt(clearTextData, hashedPwd, useIV){
    if (useIV){
      let randomBytes   = CryptoJS.lib.WordArray.random(128/8).toString();
      iv = CryptoJS.enc.Hex.parse(randomBytes);
      console.log(`iv : ${iv}`);
      // old method (wrong) which used first 16 bytes of key (hashed pwd)
      // CryptoJS.enc.Hex.parse(key);
    message = CryptoJS.AES.encrypt(clearTextData, 
              CryptoJS.enc.Hex.parse(hashedPwd),{iv: iv});
    console.log(`message.iv : ${message.iv}`);
    console.log(`message: ${message}`);
    console.log(`message.ciphertext ${message.ciphertext}`);
    console.log(`message.salt ${message.salt}`);
    }
  else{
    message = CryptoJS.AES.encrypt(clearTextData, hashedPwd );
  }
    return message.toString();
}

我将在此处解释一些细节。

encrypt() 参数

  1. clearTextData - 显然,只是您希望加密以进行保护的明文。
  2. hashedPwd - Web 客户端调用一个方法,该方法接受用户的密码并对其进行哈希处理(SHA256)。这就是哈希值。
  3. useIV - 我希望能够向用户展示用户决定不生成 IV 时的差异。CryptoJS 算法会为您生成一个 IV。它存储在调用 Crypto.AES.encrypt() 后返回的消息对象中。

您可以看到,我还console.log()了调用 Crypto.AES.encrypt() 方法后返回的message对象中的一些属性。

加密成功后

成功加密后,message 对象包含加密数据。调用message.toString()会以base64 编码的字节形式返回加密数据。这就是您在我的示例中尝试时在屏幕上看到的输出。这使得通过 HTTP 传输字节变得非常容易。

解密方会做什么?

了解解密方将如何处理这些数据很重要。

解密方将

  1. 提供(商定的)秘密 MAC 密钥
  2. 使用秘密 MAC 密钥对 IV 和EncryptedData使用 Hmac 生成 MAC。
  3. 确定他们生成的 MAC 是否与您发送给他们的 MAC(明文)匹配。

如果 MAC 不匹配?

如果 MAC 与发送给解密方的 MAC 不匹配,那么解密方将知道以下任一情况为真:

  1. 加密数据已被更改或损坏。
  2. IV 已被更改或损坏。
  3. IV 和加密数据都已损坏。

这将允许接收者知道数据可能已被泄露,并且他们应该知道数据可能不安全。

真正的认证加密

在某些情况下,攻击者可能会损坏数据,生成新的 IV 并尝试将其作为解密方发送给您。由于 MAC 只会与原始加密字节和关联的 IV 匹配,因此您将免受此类情况的侵害。这就是认证加密。

由于我们生成了 MAC,因此我们知道我们免受此问题的影响,并且我们的加密数据是安全的。

现在我将总结一下,指出一些有趣的项目。

指出一些有趣的项目

每次加密时都生成 IV

每次用户点击加密按钮时,我们都会生成一个新的随机 IV。这确保了数据的随机性和更高的安全性。但是,这也意味着您必须与解密方共享新的 IV。

这是我们用于此目的的两行代码:

let randomBytes   = CryptoJS.lib.WordArray.random(128/8).toString();
iv = CryptoJS.enc.Hex.parse(randomBytes);

这意味着,即使您保持密码和明文数据不变,输出的加密文本(base64 编码)每次都会改变。这都是数据随机化以确保安全的一部分。

观看演示

观看演示:

  1. 输入密码
  2. 输入一些明文数据(待加密)
  3. 只需连续点击加密按钮,您就会看到每次输出的加密数据都会发生变化。

加密数据

我们使用以下代码通过密码加密数据。
CryptoJS是我们用于实现 AES256 的库。

注意:您可以在上面的 JSFiddle 标签中看到资源(来自 CDN)。

message = CryptoJS.AES.encrypt(clearTextData, 
             CryptoJS.enc.Hex.parse(hashedPwd),{iv: iv});

我们调用 encrypt() 函数并传入:

  1. clearTextData - 我们要加密的原始消息
  2. hashedPwd - 我们的密码已进行 SHA256 哈希处理以确保安全,现在我们将其解析为 hex 字节
  3. iv 对象 - 一个包含名为iv的参数和我们随机生成的iv字节的对象

就这样 & RFC

就是这样!请查看并发布您对文章中任何内容的疑问或评论。

我真心希望本文能帮助大家了解 AES256 对称加密,因为它是一个非常有价值的系统,似乎得到的关注比我预期的要少。此外,我还没有见过所有这些信息集中在一个地方,我希望您觉得它很有价值。

我曾用它来加密浏览器客户端、ElectronJS 客户端等数据,并将其发布到我的 WebAPI。之后,我使用我的 Android 应用(Kotlin 应用)和其他 Web 客户端解密了数据。

历史

  • 2022 年 6 月 30 日:首次发布
© . All rights reserved.