加密:密码,摘要,盐,IV和动手指南
#编程 #encryption #c #aes

Vely and encryption
图片版权©©Sergio Mijatovic 2022

什么是加密

加密是一种将数据转换为不可用的形式的方法,只能通过解密才能使数据有用。目的是使数据仅适用于可以解密的数据(即使其可用)。通常,需要对数据进行加密,以确保在未经授权访问的情况下无法获得数据。这是攻击者设法通过授权系统并访问控制的最后一条防线。

这并不意味着所有数据都需要加密,因为通常会授权和访问系统可能足够,此外,加密和解密数据也会受到性能罚款。如果数据被加密以及何时是应用程序计划和风险评估的问题,有时也是监管要求,例如HIPAA或GDPR。

数据可以在磁盘上进行加密,例如在磁盘上或运输中,例如通过互联网通信的两个方之间的数据。

在这里,您将学习如何使用密码(也称为对称加密)加密和解密数据。此密码必须向双方交换信息。

密码,摘要,盐,iv

要正确且安全地使用加密,需要解释一些概念。

密码是用于加密的算法。例如,AES256是密码。密码的想法是大多数人在加密方面会想到的。

摘要基本上是一种哈希函数,用于在密码使用密码之前拼命和延长密码(即加密密钥)。为什么要这样做?首先,它创建了一个钥匙良好的均匀长度,可以更好地加密。它也非常适合“盐”,这是下一个要谈论的。

“盐”是击败所谓的“彩虹”桌子的一种方法。攻击者知道,如果原件是两个哈希值仍然看起来完全相同。但是,如果您将盐值添加到哈希中,那么它们就不会。它被称为“盐”,因为它与钥匙混合在一起,以产生不同的东西。现在,彩虹表将尝试将已知的哈希值与预先计算的数据匹配,以猜测密码。通常,为每个键随机生成盐并与其储存。为了匹配已知的哈希斯,攻击者将不得不将彩虹表的预定为许多随机值,这通常是不可行的。

您经常会在加密中听到有关“迭代”的消息。迭代是一个单个循环,其中将钥匙和盐混合在一起,以使猜测钥匙更加困难。这是多次这样做的,因此攻击者在计算上很难逆转钥匙,因此“迭代”(复数)。通常,所需的最小迭代数为1000,但可能与之不同。如果您从非常强的密码开始,通常需要更少。

iv(或“初始化向量”)通常是一个随机值,用于加密每个消息。现在,盐用于根据密码生产钥匙。当您已经有了密钥并且现在正在加密消息时,使用IV。 IV的目的是在加密时使相同的消息出现不同。有时,IV也具有顺序组件,因此它由随机字符串和不断增加的顺序制成。这使得“重播”攻击变得困难,这是攻击者不需要解密消息的地方;但是,“嗅探”(即发件人和接收器之间的拦截),然后重播,希望重复已经执行的操作。尽管实际上,大多数高级协议已经有一个序列,在该序列中,每条消息作为其中的一部分,是一个增加的数据包号,因此在大多数情况下,IV不需要它。

先决条件

此示例使用Vely框架的C编程语言。

请注意,使用自定义密码和摘要,以及自初始化向量和密钥缓存的明确使用自15.2起 - 如果您使用的是较早的版本,则应使用install 15.2或较早地运行这些示例。

>

>

加密示例

要在此处运行示例,请在其自己的目录中创建一个应用程序(请参阅vf有关Vely的计划经理的更多信息):

mkdir enc_example
cd enc_example
sudo vf -i -u $(whoami) enc

加密数据使用encrypt-data语句。最简单的形式是加密零端的字符串。创建一个文件âemprypt.vely'并复制以下内容:

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password"
    p-out enc_str
    @
    // Decrypt
    decrypt-data enc_str password "my_password" to define dec_str
    p-out dec_str
    @
}

您可以看到加密数据和decrypt-data的基本用法。您可以提供数据(原始或加密),密码,然后使用。数据被加密然后解密,产生原始内容。

在“源代码”中,字符串变量–enc_strâ(以char *的形式创建)将包含加密版本 - 这包含一个打开的秘密代码芝麻!dec_strâ将是解密的数据,必须完全相同。

要从命令行运行此代码,请首先制作应用程序:

vv -q

然后,请生成bash代码来运行它的请求路径是/加密,在我们的情况下,它是通过函数来​​处理的。在源文件中定义。在Vely中,这些名称始终匹配,使其易于编写,读取和执行代码。在vv中使用-râ选项来指定请求路径并获取您需要运行程序的代码:

vv -r --req="/encrypt"

这将产生类似的东西:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

复制并将其粘贴到bash壳中并执行。您将得到答复:

Content-type: text/html;charset=utf-8
Cache-Control: max-age=0, no-cache
Pragma: no-cache
Status: 200 OK

72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9
This contains a secret code, which is Open Sesame!

您在这里拥有的是加密的数据,然后使用相同的密码解密此加密数据。毫不奇怪,结果与我们首先加密的字符串匹配。

请注意,默认情况下,加密数据将以可读的十六进制形式产生加密价值,这意味着由十六进制字符组成,而不是9到9和a' fâ。这样,您可以将加密的数据存储到常规字符串中。例如,它可能转到JSON文档或数据库中的VARCHAR列,或其他任何地方。但是,您还可以生成二进制加密数据。稍等一下。

以上结果是具有适当标头的有效HTTP响应。要跳过标题,请将变量设置为vv_silent_header to是:

export VV_SILENT_HEADER=yes
/var/lib/vv/bld/enc/enc

如果您执行程序 - /var/lib/vv/bld/enc/enc/exc/enc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/exc/ext in hish oft the hith Ith Ith Ith Ith Ins nobone:

72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9
This contains a secret code, which is Open Sesame!

将数据加密到二进制结果中

在上一个示例中,所得的加密数据是人类可读的十六进制形式。您还可以创建二进制加密数据,该数据不是人类可读的字符串,也较短。为此,请使用二进制子句。用:
替换代码。

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password" \
        binary output-length define outlen
    // Save the encrypted data to a file
    write-file "encrypted_data" from enc_str length outlen
    get-app directory to define app_dir
    @Encrypted data written to file <<p-out app_dir>>/encrypted_data
    // Decrypt data
    decrypt-data enc_str password "my_password" \
        input-length outlen binary to define dec_str
    p-out dec_str
    @
}

当您想获得二进制加密数据时,您也应该在字节中获得其长度,否则您可能不知道它的结尾,因为它可能包含其中的空字符。为此,请使用输出长度子句。在此代码中,将变量的加密数据写入file``egenpted_data'',而书面的长度为“删除”字节。当文件写入没有路径的情况下,它始终写在应用程序主目录中(请参阅how_vely_works),因此您使用get-app来获取该目录。

解密数据时,请注意使用输入长度子句。它说已加密数据具有多少个字节。显然,您可以从Outlen varible中获得它,其中加密数据存储了加密数据的长度。当加密和解密被解耦时,即在单独的程序中运行时,您可以确保可提供此长度。

还请注意,当数据加密为二进制(意味着产生二进制输出)时,解密必须使用相同。

制作应用程序:

vv -q

与以前相同的运行:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

结果是:

Encrypted data written to file /var/lib/vv/enc/app/encrypted_data
This contains a secret code, which is Open Sesame!

解密的数据与原始数据完全相同。

您可以通过使用八倍垃圾箱(ODâ)Linux实用程序看到将实际的加密数据写入文件:

$ od -c /var/lib/vv/enc/app/encrypted_data
0000000 r 335 324 L 020 351 i ; 346 254 w 312 253 306 N 005
0000020 370 \t ) \n 020 235 367 317 305 t \0 224 214 270 210 315
0000040 # 307 351 216 025 274 362 033 % 253 023 7 335 306 320
0000060 224 # ! 021 252 242 325 H 300 217 # \v m V 351
0000100

你有。您会注意到数据是二进制的,实际上包含空字符。

加密二进制数据

在这些示例中加密的数据是一个字符串,即null-deledimited。您可以通过在输入长度子句中指定其长度,例如将其复制到gencrypt.vely。

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This c\000ontains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password" \
        input-length 12
    p-out enc_str
    @
    // Decrypt
    decrypt-data enc_str password "my_password" to define dec_str \
        output-length define res_len
    // Output binary data; present null char as octal \000
    int i;
    for (i = 0; i < res_len; i++) {
        if (dec_str[i] == 0) {
            p-out "\\000"
        } else {
            pf-out "%c", dec_str[i]
        }
    }
    @
}

这将在内存位置加密12个字节,而不管有任何空字符。在这种情况下,该c的含义是null字符,然后是“ Ontain”字符串,但可以是任何类型的二进制数据,例如JPG文件的内容。

在解密侧,您可以获得在输出长度子句中解密的字节数。最后,被解密的数据被证明是原始的,而空字符以典型的八进表示形式表示。

制作应用程序:

vv -q

与以前相同的运行:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

结果是:

6bea45c2f901c0913c87fccb9b347d0a
This c\000ontai

加密的值较短,因为在这种情况下数据也较短,结果与原始相匹配。

使用任何密码或摘要

默认使用的加密是来自标准OpenSSL库中的AES256SHA256哈希,这两个库都广泛用于密码学中。但是,您可以使用OpenSSL支持的任何可用密码和摘要(即哈希)(即使是您提供的自定义)。

要查看可用的算法,请在命令行中执行此操作:

#get list of cipher providers
openssl list -cipher-algorithms

#get list of digest providers
openssl list -digest-algorithms

这两个将提供密码和摘要(哈希)算法的列表。其中一些可能比Vely选择的默认值弱,而另一些则可能只是为了与旧系统的向后兼容。然而,其他人可能是很新的,并且没有足够的时间在您希望的范围内得到验证。因此,选择这些算法时要小心,并确保知道为什么要更改默认的算法。也就是说,这里是使用Camellia-256(即Camellia-256-CFB1)加密的一个例子。用:
替换代码。

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt data
    encrypt-data str to define enc_str password "my_password" \
        cipher "CAMELLIA-256-CFB1" digest "SHA3-512"
    p-out enc_str
    @
    // Decrypt data
    decrypt-data enc_str password "my_password" to define dec_str \
        cipher "CAMELLIA-256-CFB1" digest "SHA3-512"
    p-out dec_str
    @
}

制作应用程序:

vv -q

运行它:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

在这种情况下,结果是:

f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6
This contains a secret code, which is Open Sesame!

再次获得原始数据。请注意,您必须在加密数据和解密数据中使用相同的密码和摘要!

当然,您可以像以前一样使用二进制和输出长度子句产生二进制加密值。

如果您获得了加密数据的外部系统,并且您知道它们使用了哪种密码和消化,则可以匹配这些密码并使代码可互操作。 Vely使用标准OpenSL库,以便其他软件也可能。

使用盐

要加入加密,请使用盐分。您可以使用random-string语句生成随机盐。这是“ eNcrypt.vely”的代码:

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Get salt
    random-string to define rs length 16
    // Encrypt data
    encrypt-data str to define enc_str password "my_password" salt rs
    @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>>
    // Decrypt data
    decrypt-data enc_str password "my_password" salt rs to define dec_str
    p-out dec_str
    @
}

制作应用程序:

vv -q

运行几次:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc

结果:

Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd
This contains a secret code, which is Open Sesame!
Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371
This contains a secret code, which is Open Sesame!
Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917
This contains a secret code, which is Open Sesame!

您可以看到,每个加密都会生成一个随机盐值(在这种情况下为16个字节),并且每次加密值都不同,即使被加密的数据是相同的!这使得很难像这样破解加密。

请注意,如果盐为二进制值,通常会更好。在这种情况下,它是一个字符串,例如。

当然,要解密,您必须记录盐并完全像加密时一样使用盐。在此处的代码中,可变rs持有盐。如果将加密值存储在数据库中,则可能会将盐存储在旁边。

初始化向量

实际上,您不会为每个消息使用不同的盐值。它每次都会创建一个新钥匙,并且可以降低性能。而且确实没有必要:盐的使用是使每个键(甚至是相同的键)更难猜测。完成此操作后,您可能不需要再做一次或经常。

相反,您将对每个消息使用IV(初始化向量)。通常,这是一个随机字符串,使相同的消息看起来不同,并增加了破解密码的计算成本。这是“ Encrypt.vely”的新代码:

#include "vely.h"

void encrypt() {
    out-header default
    // Get salt
    random-string to define rs length 16
    // Encrypt data
    num i;
    for (i = 0; i < 10; i++) {
        random-string to define iv length 12
        encrypt-data "The same message" to define enc_str password "my_password" salt rs iterations 2000 init-vector iv cache
        @The encrypted string is <<p-out enc_str>>
        // Decrypt data
        decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to define dec_str cache
        p-out dec_str
        @
    }
}

制作应用程序:

vv -q

运行几次:

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc

结果可能是:

The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2
The same message
The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be
The same message
The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7
The same message
The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056
The same message
The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd
The same message
The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58
The same message
The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40
The same message
The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5
The same message
The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458
The same message
The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00
The same message

您可以看到加密时出现相同的消息不同,尽管当解密时又是相同的。当然,密码,盐,迭代次数和初始矢量必须相同,以均与加密和解密相同。

请注意,在加密数据和decrypt-data中使用“缓存”子句。它有效地缓存了计算的密钥(给定密码,盐,密码/消化算法和迭代次数),因此并非每次通过循环计算。使用“缓存”键一次计算一次,然后将另一个iv(在“ init-vector”子句中)用于每个消息。

如果您想偶尔重建钥匙,请使用提供布尔值的“ clear-cache”子句。如果是真的,则重新计算钥匙,否则它是单独的。请参阅encrypt-data 2。

结论

您已经学习了如何使用不同的密码,摘要,盐和IV值加密和解密数据。您还可以创建一个人类可读的加密价值和二进制输出,以及加密字符串和二进制值(如文档)。

图像版权(c)Sergio Mijatovic 2023
本文根据CC-BY-4.0许可,该文章允许在商业上复制和重新分发 - 有关更多详细信息,请参见许可。