AES-GCM加密算法

AES-GCM AES-GCM AES-GCM是基于AES-CTR模式改编的,不同于CTR的是GCM在对明文进行加密的时候还会产生tag(类似签名的东西),可以有效的抵御选择明文攻击,因为GCM首先会看tag是否合法,然后才决定是否调用decrypt oracle 进行解密。加密的流程在这篇paper讲的很清楚,我把其中的一部分拿了出来(只有两个明文块的情况) 具体的符号定义如下: 其中:H=Enck(01... 阅读详情
AI摘要

本文详细介绍了AES-GCM模式,它是AES加密与GMAC消息认证码的结合,提供了加密和消息完整性的双重保障。文章首先解释了对称加密算法AES、CTR模式以及MAC的概念,接着探讨了ECB和CTR模式的优缺点,并引出了MAC和GMAC的重要性。在讲解GCM模式的工作原理后,展示了OpenSSL中如何使用EVP接口执行AES-GCM的加密和解密操作,以及附加数据的处理。此外,还给出了AES加密解密的C++示例代码,强调了IV在加密解密过程中的关键作用。

AES是一种对称加密算法,它的相关概念在此不赘述。

GCM ( Galois/Counter Mode) 指的是该对称加密采用Counter模式,并带有GMAC消息认证码。

在详细介绍AES-GCM之前,我们先了解一些相关概念。

 

下文中出现的符号:

 

Ek使用秘钥k对输入做对称加密运算
XOR异或运算
Mh将输入与秘钥h在有限域GF(2^128)上做乘法

 

 

ECB( Electronic Mode 电子密码本模式)

当我们有一段明文,需要对其进行AES加密时,需要对明文进行分组,分组长度可为128,256,或512bits。采用ECB模式的分组密码算法加密过程如下图:

由上图可以看出,明文中重复的排列会反映在密文中。

并且,当密文被篡改时,解密后对应的明文分组也会出错,且解密者察觉不到密文被篡改了。也就是说,ECB不能提供对密文的完整性校验。
因此,在任何情况下都不推荐使用ECB模式。

 

 

CTR ( CounTeR 计数器模式)

在计数器模式下,我们不再对密文进行加密,而是对一个逐次累加的计数器进行加密,用加密后的比特序列与明文分组进行 XOR得到密文。过程如下图:


计数器模式下,每次与明文分组进行XOR的比特序列是不同的,因此,计数器模式解决了ECB模式中,相同的明文会得到相同的密文的问题。CBC,CFB,OFB模式都能解决这个问题,但CTR的另两个优点是:1)支持加解密并行计算,可事先进行加解密准备;2)错误密文中的对应比特只会影响明文中的对应比特等优点。
但CTR仍然不能提供密文消息完整性校验的功能。
有的人可能会想到,如果将密文的hash值随密文一起发送,密文接收者对收到的密文计算hash值,与收到的hash值进行比对,这样是否就能校验消息的完整性呢?

再仔细想想,就能发现这其中的漏洞。当篡改者截获原始的密文消息时,先篡改密文,而后计算篡改后的密文hash, 替换掉原始消息中的密文hash。这样,消息接收者仍然没有办法发现对源密文的篡改。可见,使用单向散列函数计算hash值仍然不能解决消息完整性校验的问题。

MAC  ( Message Authentication Code, 消息验证码)

想要校验消息的完整性,必须引入另一个概念:消息验证码。消息验证码是一种与秘钥相关的单项散列函数。

密文的收发双发需要提前共享一个秘钥。密文发送者将密文的MAC值随密文一起发送,密文接收者通过共享秘钥计算收到密文的MAC值,这样就可以对收到的密文做完整性校验。当篡改者篡改密文后,没有共享秘钥,就无法计算出篡改后的密文的MAC值。

如果生成密文的加密模式是CTR,或者是其他有初始IV的加密模式,别忘了将初始的计时器或初始向量的值作为附加消息与密文一起计算MAC。

 

GMAC ( Galois message authentication code mode, 伽罗瓦消息验证码 )

对应到上图中的消息认证码,GMAC就是利用伽罗华域(Galois Field,GF,有限域)乘法运算来计算消息的MAC值。假设秘钥长度为128bits, 当密文大于128bits时,需要将密文按128bits进行分组。应用流程如下图:

 

 

GCM( Galois/Counter Mode ) 

GCM中的G就是指GMAC,C就是指CTR。

GCM可以提供对消息的加密和完整性校验,另外,它还可以提供附加消息的完整性校验。在实际应用场景中,有些信息是我们不需要保密,但信息的接收者需要确认它的真实性的,例如源IP,源端口,目的IP,IV,等等。因此,我们可以将这一部分作为附加消息加入到MAC值的计算当中。下图的Ek表示用对称秘钥k对输入做AES运算。最后,密文接收者会收到密文、IV(计数器CTR的初始值)、MAC值。

AES(Advanced Encryption Standard)即高级加密标准,由美国国家标准和技术协会(NIST)于2000年公布,它是一种对称加密算法。关于AES的更多介绍可以参考:https://blog.youkuaiyun.com/fengbingchun/article/details/100139524

AES的GCM(Galois/Counter Mode)模式本质上是AES的CTR模式(计数器模式)加上GMAC(Galois Message Authentication Code, 伽罗华消息认证码)进行哈希计算的一种组合模式。GCM可以提供对消息的加密和完整性校验,另外,它还可以提供附加消息的完整性校验。

OpenSSL中的EVP接口支持执行经过身份验证的加密和解密的功能,以及将未加密的关联数据附加到消息的选项。这种带有关联数据的身份验证加密(AEAD, Authenticated-Encryption with Associated-Data)方案通过对数据进行加密来提供机密性,并通过在加密数据上创建MAC标签来提供真实性保证。

Key:对称密钥,它的长度可以为128、192、256bits,用来加密明文的密码。

IV(Initialisation Vector):初始向量,它的选取必须随机。通常以明文的形式和密文一起传送。它的作用和MD5的”加盐”有些类似,目的是防止同样的明文块,始终加密成同样的密文块。

AAD(Additional Authenticated Data):附加身份验证数据。AAD数据不需要加密,通常以明文形式与密文一起传递给接收者。

Mac tag(MAC标签):将确保数据在传输和存储过程中不会被意外更改或恶意篡改。该标签随后在解密操作期间使用,以确保密文和AAD未被篡改。在加密时,Mac tag由明文、密钥Key、IV、AAD共同产生。

以下为测试代码:

namespace {
 
static const unsigned char gcm_key[] = { // 32 bytes, Key
    0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,
    0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,
    0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f
};
 
static const unsigned char gcm_iv[] = { // 12 bytes, IV(Initialisation Vector)
    0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84
};
 
// Additional Authenticated Data(AAD): it is not encrypted, and is typically passed to the recipient in plaintext along with the ciphertext
static const unsigned char gcm_aad[] = { // 16 bytes
    0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43,
    0x7f, 0xec, 0x78, 0xde
};
 
std::unique_ptr<unsigned char[]> aes_gcm_encrypt(const char* plaintext, int& length, unsigned char* tag)
{
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    // Set cipher type and mode
    EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
    // Set IV length if default 96 bits is not appropriate
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), nullptr);
    // Initialise key and IV
    EVP_EncryptInit_ex(ctx, nullptr, nullptr, gcm_key, gcm_iv);
    // Zero or more calls to specify any AAD
    int outlen;
    EVP_EncryptUpdate(ctx, nullptr, &outlen, gcm_aad, sizeof(gcm_aad));
    unsigned char outbuf[1024];
    // Encrypt plaintext
    EVP_EncryptUpdate(ctx, outbuf, &outlen, (const unsigned char*)plaintext, strlen(plaintext));
    length = outlen;
    std::unique_ptr<unsigned char[]> ciphertext(new unsigned char[length]);
    memcpy(ciphertext.get(), outbuf, length);
    // Finalise: note get no output for GCM
    EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
    // Get tag
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, outbuf);
    memcpy(tag, outbuf, 16);
    // Clean up
    EVP_CIPHER_CTX_free(ctx);
    return ciphertext;
}
 
std::unique_ptr<unsigned char[]> aes_gcm_decrypt(const unsigned char* ciphertext, int& length, const unsigned char* tag)
{
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    // Select cipher
    EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
    // Set IV length, omit for 96 bits
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), nullptr);
    // Specify key and IV
    EVP_DecryptInit_ex(ctx, nullptr, nullptr, gcm_key, gcm_iv);
    int outlen;
    // Zero or more calls to specify any AAD
    EVP_DecryptUpdate(ctx, nullptr, &outlen, gcm_aad, sizeof(gcm_aad));
    unsigned char outbuf[1024];
    // Decrypt plaintext
    EVP_DecryptUpdate(ctx, outbuf, &outlen, ciphertext, length);
    // Output decrypted block
    length = outlen;
    std::unique_ptr<unsigned char[]> plaintext(new unsigned char[length]);
    memcpy(plaintext.get(), outbuf, length);
    // Set expected tag value
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (void*)tag);
    // Finalise: note get no output for GCM
    int rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
    // Print out return value. If this is not successful authentication failed and plaintext is not trustworthy.
    fprintf(stdout, "Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
    EVP_CIPHER_CTX_free(ctx);
    return plaintext;
}
 
} // namespace
 
int test_openssl_aes_gcm()
{
    /* reference:
        https://github.com/openssl/openssl/blob/master/demos/evp/aesgcm.c
        https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
    */
    fprintf(stdout, "Start AES GCM 256 Encrypt:\n");
    const char* plaintext = "1234567890ABCDEFG!@#$%^&*()_+[]{};':,.<>/?|";
    fprintf(stdout, "src plaintext: %s, length: %d\n", plaintext, strlen(plaintext));
    int length = 0;
    std::unique_ptr<unsigned char[]> tag(new unsigned char[16]);
    std::unique_ptr<unsigned char[]> ciphertext = aes_gcm_encrypt(plaintext, length, tag.get());
    fprintf(stdout, "length: %d, ciphertext: ", length);
    for (int i = 0; i < length; ++i)
        fprintf(stdout, "%02x ", ciphertext.get()[i]);
    fprintf(stdout, "\nTag: ");
    for (int i = 0; i < 16; ++i)
        fprintf(stdout, "%02x ", tag.get()[i]);
    fprintf(stdout, "\n");
 
    fprintf(stdout, "\nStart AES GCM 256 Decrypt:\n");
    std::unique_ptr<unsigned char[]> result = aes_gcm_decrypt(ciphertext.get(), length, tag.get());
    fprintf(stdout, "length: %d, decrypted plaintext: ", length);
    for (int i = 0; i < length; ++i)
        fprintf(stdout, "%c", result.get()[i]);
    fprintf(stdout, "\n");
 
    if (strncmp(plaintext, (const char*)result.get(), length) == 0) {
        fprintf(stdout, "decrypt success\n");
        return 0;
    } else {
        fprintf(stderr, "decrypt fail\n");
        return -1;
    }
}
在Windows上执行结果如下:

以上是在OpenSSL较老的版本1.0.1g上执行的,在最新版1.1.1g上以上代码也同样可以执行。

在Windows上编译1.1.1g版本源码执行命令如下:no-asm选项为不开启汇编模式,编译release则将debug-VC-WIN64A改为VC-WIN64即可。

perl Configure debug-VC-WIN64A no-asm --prefix=D:\DownLoad\openssl\win64_debug
nmake
nmake install
在Linux上编译1.1.1g版本源码执行命令如下:

./config --prefix=/home/sensetime/Downloads/openssl/install
make
make insall
在1.1.1g源码的demos/evp目录下给出了gcm的示例aesgcm.c,这里将以上测试代码新加为aesgcm2.cpp,编译脚本如下:

#! /bin/bash
 
g++ -o test_aesgcm aesgcm.c -L ../../ -lcrypto -lssl -I../../include
g++ -o test_aesgcm2 aesgcm2.cpp -std=c++11 -L ../../ -lcrypto -lssl -I../../include
 
g++ -o test_aesccm aesccm.c -L ../../ -lcrypto -lssl -I../../include
执行结果如下:注意如果在Windows上执行aesgcm2.cpp,需要额外的 #include <openssl/applink.c> 

GitHub:https://github.com//fengbingchun/OpenSSL_Test
----------------------------------------------------------------------------------------------------------------

转自:http://www.thinkemb.com/wordpress/?p=18

参考:http://blog.youkuaiyun.com/shuanyancao/article/details/8985963

http://stackoverflow.com/questions/18152913/aes-aes-cbc-128-aes-cbc-192-aes-cbc-256-encryption-decryption-with-openssl-c

 

Openssl是很常见的C接口的库,个人觉得易用。以下是AES加密的使用备忘。如果你有一定的密码学基础,那么就很好理解。代码是从网上弄下来的(原始地址已经忘记了),然后在尝试的过程中改了一点东西。其它的cbc、cfb、ecb加密方式的用法都是类似的,只是函数名有点区别,就不一一列举了。

【yasi】IV: Initialization Vector,即初始化向量

一、接口简介

 

 
  1. //设置加密密钥,使用字符缓冲区

  2. int AES_set_encrypt_key(

  3. const unsigned char *userKey,

  4. const int bits,

  5. AES_KEY *key);

  6.  
  7. //设置解密密钥,同样适用字符缓冲区

  8. int AES_set_decrypt_key(

  9. const unsigned char *userKey,

  10. const int bits,

  11. AES_KEY *key);

  12.  
  13. //加解密的接口,通过最后的enc来区分是加密还是解密操作

  14. //每次执行AES_cbc_encrypt后,iv(向量)会被更新,

  15. //所以需要自己保存它。

  16. void AES_cbc_encrypt(

  17. const unsigned char *in,

  18. unsigned char *out,

  19. const unsigned long length,

  20. const AES_KEY *key,

  21. unsigned char *ivec,

  22. const int enc);

?

 

二、一个简单的Makefile

【yasi】针对CentOS环境,做了修改

 

LNK_OPT = -g -L/usr/lib64/ -lssl

all:
        rm -f codec
        g++ -g aes_codec.cpp -o codec $(LNK_OPT)

clean:
        rm -f codec


三、示例代码

【yasi】对源链接的代码做了点修改:随机明码 改 静态明码

 

 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include <openssl/aes.h>

  4. #include <openssl/rand.h>

  5.  
  6. /* file testaes.cpp */

  7.  
  8. static void hexdump(

  9. FILE *f,

  10. const char *title,

  11. const unsigned char *s,

  12. int l)

  13. {

  14. int n = 0;

  15.  
  16. fprintf(f, "%s", title);

  17. for (; n < l; ++n) {

  18. if ((n % 16) == 0) {

  19. fprintf(f, "\n%04x", n);

  20. }

  21. fprintf(f, " %02x", s[n]);

  22. }

  23.  
  24. fprintf(f, "\n");

  25. }

  26.  
  27. int main(int argc, char **argv)

  28. {

  29. //128bits key.

  30. unsigned char rkey[16];

  31. //Internal key.

  32. AES_KEY key;

  33.  
  34. //Testdata.

  35. // [yasi] Make static content instead of random text

  36. unsigned char plaintext[AES_BLOCK_SIZE * 4] =

  37. {

  38. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i',

  39. '0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7',

  40. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i',

  41. '0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7'

  42. };

  43. unsigned char ciphertext[AES_BLOCK_SIZE * 4];

  44. unsigned char checktext[AES_BLOCK_SIZE * 4];

  45.  
  46. //Init vector.

  47. unsigned char iv[AES_BLOCK_SIZE * 4];

  48. //Save vector.

  49. unsigned char saved_iv[AES_BLOCK_SIZE * 4];

  50.  
  51. int nr_of_bits = 0;

  52. int nr_of_bytes = 0;

  53.  
  54. //Zeror buffer.

  55. memset(ciphertext, 0, sizeof ciphertext);

  56. memset(checktext, 0, sizeof checktext);

  57.  
  58. //Generate random

  59. RAND_pseudo_bytes(rkey, sizeof rkey);

  60. RAND_pseudo_bytes(saved_iv, sizeof saved_iv);

  61.  
  62. hexdump(stdout, "== rkey ==",

  63. rkey,

  64. sizeof(rkey));

  65. hexdump(stdout, "== iv ==",

  66. saved_iv,

  67. sizeof(saved_iv));

  68. printf("\n");

  69.  
  70. hexdump(stdout, "== plaintext ==",

  71. plaintext,

  72. sizeof(plaintext));

  73. printf("\n");

  74.  
  75. //Entrypt

  76. memcpy(iv, saved_iv, sizeof(iv));

  77. nr_of_bits = 8 * sizeof(rkey);

  78. AES_set_encrypt_key(rkey, nr_of_bits, &key);

  79. nr_of_bytes = sizeof(plaintext);

  80. AES_cbc_encrypt(plaintext,

  81. ciphertext,

  82. nr_of_bytes,

  83. &key,

  84. iv,

  85. AES_ENCRYPT);

  86.  
  87. hexdump(stdout, "== ciphertext ==",

  88. ciphertext,

  89. sizeof(ciphertext));

  90. printf("\n");

  91. // [yasi] iv is changed in encryption

  92. hexdump(stdout, "== iv changed ==",

  93. iv,

  94. sizeof(iv));

  95. printf("\n");

  96.  
  97. //Decrypt

  98. memcpy(iv, saved_iv, sizeof(iv)); // [yasi] without this line, decrypt will fail because iv is changed in encryption

  99. nr_of_bits = 8 * sizeof(rkey);

  100. AES_set_decrypt_key(rkey, nr_of_bits, &key);

  101. nr_of_bytes = sizeof(ciphertext);

  102.  
  103. AES_cbc_encrypt(ciphertext,

  104. checktext,

  105. nr_of_bytes,

  106. &key, iv,

  107. AES_DECRYPT);

  108. hexdump(stdout, "== checktext ==",

  109. checktext,

  110. sizeof(checktext));

  111. printf("\n");

  112.  
  113. return 0;

  114. }

 

【yasi 运行结果】

[root@ampcommons02 aes-codec]# ./codec
== rkey ==
0000 81 ac 9b 38 1c 02 c5 c8 1d 7c a0 3f 87 be f2 c6
== iv ==
0000 34 a2 f1 f5 f3 93 76 32 cd 77 ad fb c8 82 f2 1b
0010 f3 cc 51 f6 35 f3 49 8d 44 97 8c 2c 89 50 0d d7
0020 68 21 d7 2f 0a 90 29 c1 dd c9 39 bc 7c 4f 18 2f
0030 04 cc 42 5e 84 8e fe a9 c5 49 00 9f 30 55 94 c0

== plaintext ==
0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

== ciphertext ==
0000 c4 63 72 29 21 28 7b a2 27 24 4a e4 bb 95 1a d1
0010 b8 13 0e 77 0c 8a a4 09 2f ca 85 43 41 b5 5b d5
0020 a3 60 92 58 5b dd 45 0c e2 62 af f9 43 81 d7 06
0030 41 8e 85 28 3e eb 72 b5 ee 84 8c 27 7e 67 20 f6

== iv changed ==
0000 41 8e 85 28 3e eb 72 b5 ee 84 8c 27 7e 67 20 f6
0010 f3 cc 51 f6 35 f3 49 8d 44 97 8c 2c 89 50 0d d7
0020 68 21 d7 2f 0a 90 29 c1 dd c9 39 bc 7c 4f 18 2f
0030 04 cc 42 5e 84 8e fe a9 c5 49 00 9f 30 55 94 c0

== checktext ==
0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

可见,encryption之后,IV立即被修改了。所以,为了能正确decrypt,在decrypt时,必须使用先前encrypt时的IV,即代码第98行。

 

【第二次运行的结果】

[root@ampcommons02 aes-codec]# ./codec
== rkey ==
0000 29 68 75 4d a5 9e 83 9a ed f8 ec bc 2e b8 09 7e
== iv ==
0000 b8 21 09 de 8f 58 6e be 73 be a7 10 fb 91 87 65
0010 65 9c d7 0e 4c 88 d2 65 ae de 0b 49 40 c7 75 df
0020 19 69 53 0b 11 5d ac e7 08 f6 ae df 16 66 e0 13
0030 75 41 f7 bb be 56 a1 dd a7 3e fb 4e 5d 9e e4 a2

== plaintext ==
0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

== ciphertext ==
0000 5e 85 9c e8 65 d6 3b f9 03 9a a0 b5 78 bd f6 d4
0010 11 70 94 c1 c3 78 9a 1d 12 9a 84 48 3a 70 88 13
0020 d6 b5 bf c6 e8 e1 76 dc 3c b9 b0 4e b9 bb c4 74
0030 35 3d ac fc 29 e3 a0 64 d7 76 ab 76 c7 af dd 39

== iv changed ==
0000 35 3d ac fc 29 e3 a0 64 d7 76 ab 76 c7 af dd 39
0010 65 9c d7 0e 4c 88 d2 65 ae de 0b 49 40 c7 75 df
0020 19 69 53 0b 11 5d ac e7 08 f6 ae df 16 66 e0 13
0030 75 41 f7 bb be 56 a1 dd a7 3e fb 4e 5d 9e e4 a2

== checktext ==
0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

可见,两次运行,虽然encrypt的明码是一样的,但encrypt出来的结果是不同的。随机明码 改 静态明码 的目的就是为了比较两次encrypt的结果。即,相同的明码用不同的IV做encrypt,结果是不同的。

 

【屏蔽第98行重新编译后的执行结果】

[root@ampcommons02 aes-codec]# ./codec
== rkey ==
0000 69 ef eb 49 25 5a c2 5e 0d 77 8a cb e6 fe ad 1d
== iv ==
0000 8e 05 8c 50 2f 69 9d fb 64 3e cd e6 2d 38 26 1c
0010 6e 21 00 e6 32 3f c6 ca 93 8b c1 e3 47 9a bd 81
0020 d7 e5 0b 63 dc f8 9d 1f 13 49 35 25 70 4e 64 c2
0030 ea 0c d0 78 e7 6c 65 64 41 4d db 2b 50 4d b4 06

== plaintext ==
0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

== ciphertext ==
0000 a4 ed ba 4b 9f e9 74 bd 6d f6 03 76 79 9f 17 4f
0010 0c cd f3 b8 da 69 44 81 c9 f2 8b 03 83 0d 9d 77
0020 12 48 ea 46 3f eb 58 fd 48 c5 cc 2d 74 6c 99 4f
0030 93 bd 0d 06 f3 55 40 11 cb e7 d4 29 3b 8f 15 76

== iv changed ==
0000 93 bd 0d 06 f3 55 40 11 cb e7 d4 29 3b 8f 15 76
0010 6e 21 00 e6 32 3f c6 ca 93 8b c1 e3 47 9a bd 81
0020 d7 e5 0b 63 dc f8 9d 1f 13 49 35 25 70 4e 64 c2
0030 ea 0c d0 78 e7 6c 65 64 41 4d db 2b 50 4d b4 06

== checktext ==
0000 7c da e2 32 b9 5a ba 83 ce bb 7a ab 73 d1 54 03
0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37

可见,decrypt时使用和encrypt时不同的IV,不能正确decrypt出先前的明码。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

【yasi】 Openssl官方wiki, 《EVP Symmetric Encryption and Decryption》 中,有个的简单例子,整理如下(Linux下运行): (代码下载)

algo_aes.h

 
  1. #ifndef ALGO_AES_H

  2. #define ALGO_AES_H

  3.  
  4. int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,

  5. unsigned char *iv, unsigned char *ciphertext);

  6.  
  7. int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,

  8. unsigned char *iv, unsigned char *plaintext);

  9.  
  10. #endif


algo_aes.c

 
  1. #include <stdlib.h>

  2. #include <stdio.h>

  3. #include "algo_aes.h"

  4. #include <openssl/evp.h>

  5.  
  6. void handleErrors(void)

  7. {

  8. ERR_print_errors_fp(stderr);

  9. abort();

  10. }

  11.  
  12. int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,

  13. unsigned char *iv, unsigned char *ciphertext)

  14. {

  15. EVP_CIPHER_CTX *ctx;

  16.  
  17. int len;

  18.  
  19. int ciphertext_len;

  20.  
  21. /* Create and initialise the context */

  22. if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

  23.  
  24. /* Initialise the encryption operation. IMPORTANT - ensure you use a key

  25. * and IV size appropriate for your cipher

  26. * In this example we are using 256 bit AES (i.e. a 256 bit key). The

  27. * IV size for *most* modes is the same as the block size. For AES this

  28. * is 128 bits */

  29. if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))

  30. handleErrors();

  31.  
  32. /* Provide the message to be encrypted, and obtain the encrypted output.

  33. * EVP_EncryptUpdate can be called multiple times if necessary

  34. */

  35. if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))

  36. handleErrors();

  37. ciphertext_len = len;

  38.  
  39. /* Finalise the encryption. Further ciphertext bytes may be written at

  40. * this stage.

  41. */

  42. if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();

  43. ciphertext_len += len;

  44.  
  45. /* Clean up */

  46. EVP_CIPHER_CTX_free(ctx);

  47.  
  48. return ciphertext_len;

  49. }

  50.  
  51. int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,

  52. unsigned char *iv, unsigned char *plaintext)

  53. {

  54. EVP_CIPHER_CTX *ctx;

  55.  
  56. int len;

  57.  
  58. int plaintext_len;

  59.  
  60. /* Create and initialise the context */

  61. if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

  62.  
  63. /* Initialise the decryption operation. IMPORTANT - ensure you use a key

  64. * and IV size appropriate for your cipher

  65. * In this example we are using 256 bit AES (i.e. a 256 bit key). The

  66. * IV size for *most* modes is the same as the block size. For AES this

  67. * is 128 bits */

  68. if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))

  69. handleErrors();

  70.  
  71. /* Provide the message to be decrypted, and obtain the plaintext output.

  72. * EVP_DecryptUpdate can be called multiple times if necessary

  73. */

  74. if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))

  75. handleErrors();

  76. plaintext_len = len;

  77.  
  78. /* Finalise the decryption. Further plaintext bytes may be written at

  79. * this stage.

  80. */

  81. if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();

  82. plaintext_len += len;

  83.  
  84. /* Clean up */

  85. EVP_CIPHER_CTX_free(ctx);

  86.  
  87. return plaintext_len;

  88. }


main.c

 
  1. #include "algo_aes.h"

  2. #include <stdio.h>

  3. #include <string.h>

  4. //#include <openssl/evp.h>

  5.  
  6. int main(int arc, char *argv[])

  7. {

  8. /* Set up the key and iv. Do I need to say to not hard code these in a

  9. * real application? :-)

  10. */

  11.  
  12. /* A 256 bit key */

  13. unsigned char *key = "01234567890123456789012345678901";

  14.  
  15. /* A 128 bit IV */

  16. unsigned char *iv = "01234567890123456";

  17.  
  18. /* Message to be encrypted */

  19. unsigned char *plaintext =

  20. "The quick brown fox jumps over the lazy dog1234";

  21.  
  22. /* Buffer for ciphertext. Ensure the buffer is long enough for the

  23. * ciphertext which may be longer than the plaintext, dependant on the

  24. * algorithm and mode

  25. */

  26. unsigned char ciphertext[64];

  27.  
  28. /* Buffer for the decrypted text */

  29. unsigned char decryptedtext[64];

  30.  
  31. int decryptedtext_len, ciphertext_len;

  32.  
  33. /* Initialise the library */

  34. /* ERR_load_crypto_strings();

  35. OpenSSL_add_all_algorithms();

  36. OPENSSL_config(NULL);*/

  37.  
  38. printf("Plaintext is:\n%s~\n", plaintext);

  39.  
  40. /* Encrypt the plaintext */

  41. ciphertext_len = encrypt(plaintext, strlen(plaintext), key, iv,

  42. ciphertext);

  43.  
  44. /* Do something useful with the ciphertext here */

  45. printf("Ciphertext is %d bytes long:\n", ciphertext_len);

  46. BIO_dump_fp(stdout, ciphertext, ciphertext_len);

  47.  
  48. /* Decrypt the ciphertext */

  49. decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, iv,

  50. decryptedtext);

  51.  
  52. /* Add a NULL terminator. We are expecting printable text */

  53. decryptedtext[decryptedtext_len] = '\0';

  54.  
  55. /* Show the decrypted text */

  56. printf("Decrypted text is:\n");

  57. printf("%s~\n", decryptedtext);

  58.  
  59. /* Clean up */

  60. EVP_cleanup();

  61. ERR_free_strings();

  62.  
  63. return 0;

  64. }

Mekefile:

OBJ_DIR = ./obj
BIN_DIR = ./bin
SRC_DIR = ./
OBJS = \
	$(OBJ_DIR)/algo_aes.o \
	$(OBJ_DIR)/main.o
TARGET = $(BIN_DIR)/main
INC_OPT = -I./
LNK_OPT = -lssl 

$(TARGET) : clean chkobjdir chkbindir $(OBJS)
	gcc -g -o $@ $(OBJS) $(LNK_OPT)

$(OBJ_DIR)/%.o : $(SRC_DIR)/%.c
	gcc -g $(INC_OPT) -c -o $@ $<

chkobjdir :
	@if test ! -d $(OBJ_DIR) ; \
	then \
		mkdir $(OBJ_DIR) ; \
	fi

chkbindir :
	@if test ! -d $(BIN_DIR) ; \
	then \
		mkdir $(BIN_DIR) ; \
	fi

clean :
	-rm -f $(TARGET)
	-rm -rf $(OBJ_DIR)

运行结果:

Plaintext is:
The quick brown fox jumps over the lazy dog1234~
Ciphertext is 48 bytes long:
0000 - e0 6f 63 a7 11 e8 b7 aa-9f 94 40 10 7d 46 80 a1   .oc.......@.}F..
0010 - 17 99 43 80 ea 31 d2 a2-99 b9 53 02 d4 39 b9 70   ..C..1....S..9.p
0020 - e9 29 d5 a8 03 bd 71 31-b8 c3 6f 3d 39 3a 3d 3d   .)....q1..o=9:==
Decrypted text is:
The quick brown fox jumps over the lazy dog1234~

注意:(参考)

AES算法的块(block)的长度固定为16字节。假设一个字符串在AES加密前的长度为cleanLen,加密后的长度为cipherLen,则二者有下面的关系,其中的“/”是整除。

cipherLen = (clearLen/16 + 1) * 16

比如:(注意第二行,即使48刚好能被16整除,也要额外追加一个16字节的块)

clearLencipherLen 
4748
4864
4964

 

可见,对于AES算法:

1)加密后的长度>=加密前的长度

2)解密后的长度<=解密前的长度

这对于写代码时的指导意义在于:

1)假如我们要做AES加密的字符串为“The quick brown fox jumps over the lazy dog”,它的长度(不带/0)为43字节,则我们可以计算出加密后的串的长度为 (43 / 16 + 1) * 16 = 48 字节,于是就可以申明加密后的数组如下

unsigned char ciphertextp[48];

2)假如我们知道AES加密后的串的长度为64字节,那么就可以申明解密后的数组如下

unsigned char decryptedtext[64];

 

【密码算法 之七】GCM 浅析 GCM 全称为 Galois/Counter Mode,其中 G 是指 GMAC,C 是指 CTR 模式,可以将 GCM 认为是认证模式的一种,提供认证和加密两种功能。GCM模式的分组大小为128bit。在进行GCM原理分析之前,首先应该了解一下GHASH函数及GCTR函数。GCM运算所需的对称加密算法的分组长度必须是128bit(DES\3DES不满足添加,AES\SM4满足条件);加密时,在计算MAC之前必别将附加数据(A)及密文(C)分别进行Block对齐; 阅读详情

相关推荐

AES-GCM算法

AES-GCM算法简介 AES-GCM算法是带认证和加密的算法,同时可以对给定的原文,生成加密数据和认证码。参数如下: 1)带加密的原文、2)存储加密后密文、3)IV向量、4)生成的消息验证码tag、5)额外的消息认证数据aad,通信双方需要共享; 整个AES-GCM算法代码如下: #include &lt;stdio.h&gt; #include &lt;string.h&gt; #i...

daizhongyin的专栏 1万+

什么是 AES-GCM加密算法

AES是一种对称加密算法,它的相关概念在此不赘述。 GCM ( Galois/Counter Mode) 是对称加密的一种加密模式。 在介绍AES-GCM之前,我们先了解一些相关概念。 下文中出现的符号: Ek 使用秘钥k对输入做对称加密运算 XOR 异或运算 Mh 将输入与秘钥h在有限域GF(2^128)上做乘法

T0mato_的博客 25万+

AES-GCM数据加密算法

本文介绍了AES-GCM加密算法在数据传输安全中的应用。AES-GCM结合了加密和认证功能,通过计数器模式和伽罗瓦运算实现高效安全的数据保护。文章详细说明了在Windows环境下使用OpenSSL实现AES-GCM的配置方法,包括VS2022工程设置和动态库引用。同时提供了完整的代码示例,展示了AES-GCM的加密解密实现过程,包括随机IV生成、数据加密/解密以及完整性验证等关键功能。该方案适用于需要防止数据篡改的网络传输场景。

lemontree1945的博客 853

AES-GCM加密算法的简单介绍

一.什么是AES加密? 常见的加密主要分为两类:对称加密和非对称加密,AES加密就是对称加密的一种,即加密和解密使用相同的一把密钥。它的全称是Advanced Encryption Standard(高级加密标准),主要是用来取代DES加密算法,目前已经被全世界广泛采用,各大处理器厂商也在各自的CPU中,集成了专门的AES指令集,从而在硬件层面提升了AES加解密的速度。 具体加密过程:十分钟读懂AES加密算法 二.AES加密基本构成 1.对称加密相关概念 明文P(plainText):未经加密的数据 密钥K

qq_39500214的博客 1万+

ESP32-CAM图传小车:端边协同的嵌入式视觉系统设计

在物联网边缘智能场景中,嵌入式视觉系统需平衡实时性、算力与成本。其核心在于理解图像采集、硬件编码、低延迟传输与分层计算的基本原理——OV2640等CMOS传感器配合JPEG硬件编码可显著降低主控负载;TCP流式协议比HTTP/RTSP更适配资源受限MCU;而‘计算卸载’架构将OpenCV等重负载移至Android端,使ESP32专注实时控制与传输,既发挥FreeRTOS任务调度优势,又规避了PSRAM带宽与内存瓶颈。该模式广泛适用于教育机器人、智能巡检及DIY视觉终端等场景,本文以WiFi图传坦克小车为载体

weixin_42524864的博客 370

手把手用ROS实现Ego-Planner动态避障:无人机撞树问题终结方案

本文详细介绍了如何在ROS和Gazebo仿真环境中,通过配置和调优Ego-Planner算法,实现无人机的动态避障功能。文章深入解析了其基于局部梯度优化的核心思想,提供了从环境搭建、参数详解到代码集成的完整实践指南,并分享了实战排错经验,旨在彻底解决无人机在复杂动态场景中的碰撞问题。

weixin_29031161的博客 1624

AEAD:AES-GCM简介

因此,需要在对称加密算法上增加一层验证机制,常见思路为使用加密算法进行加密保证机密性,使用摘要算法计算MAC保证完整性和真实性,这就涉及到摘要计算和加密计算的前后顺序和参与计算的数据、密钥等,使用起来略微麻烦。认证加密输出包括密文(C)和认证tag(T),其中Tag长度可以为2、4、6、8、10、12、14、16。认证加密需要两个输入:密文(C)和认证Tag(T),输出为明文P或者是特定错误码,例如无效Tag不予解密。对于IV,建议将实现限制为96位长度,以提升操作性、效率和设计的简单性。

认真搞搞汽车MCU 4189

三维视觉智能焊接系统:免示教技术的工业应用实践

本文深入探讨了三维视觉智能焊接系统中免示教技术的工业应用实践。该系统通过三维视觉相机实时扫描工件,自动识别焊缝并生成轨迹,将产线换型时间从数小时缩短至几分钟,显著提升了生产柔性。文章详细解析了其核心硬件、算法及工厂部署流程,并展望了与数字孪生、工艺智能化融合的未来趋势。

weixin_29091105的博客 430

AES-GCM加密算法C语言源码

AES-GCM加密算法C语言源码 去发现同类优质开源项目:https://gitcode.com/ 简介 AES-GCM(高级加密标准-伽罗瓦/计数器模式)是一种高效的对称加密算法,它不仅提供加密功能,还具备数据完整性验证的能力。本仓库提供了一种经过验证的AES-GCM算法的C语言源码实现,便于开发者使用和集成到自己的项目中。 文件内容 GCM源码.rar:包含AES-GCM算法的C语言源码文件...

gitblog_06707的博客 560

手把手教你学simulink(8.39)--基于Simulink的步进电机(Stepper Motor)仿真实例

通过这个仿真实例,你可以学习如何使用Simulink构建和仿真步进电机(Stepper Motor)系统。你可以根据实际需求调整模型和参数,进行更复杂的仿真和分析。希望这个例子对你有所帮助,祝你学习愉快!

MHD0815的博客 1050

002、搭建你的第一个Agent开发环境:Python与核心库安装

在上一篇《Agent智能体开发入门:概念、应用与前景》中,我们描绘了Agent智能体的宏伟蓝图,理解了其作为“数字员工”的巨大潜力。然而,许多开发者在迈出第一步时,常常被复杂的依赖、版本冲突和晦涩的配置所劝退,空有想法却无从下手。这种感觉就像拿到了一张藏宝图,却找不到出发的港口。本文正是你的“启航指南”。我们将彻底解决这个痛点,通过一个清晰、可复现的流程,为你搭建一个专为Agent开发量身定制的Python环境。一个纯净、隔离的Python虚拟环境,避免项目间的“依赖地狱”。一套经过验证的核心工具链。

G_RE_RE的博客 111

AES-GCM加密算法C语言源码:高效对称加密解决方案

AES-GCM加密算法C语言源码:高效对称加密解决方案 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在当今数字化时代,信息安全成为软件开发中至关重要的一环。AES-GCM加密算法C语言源码项目为开发者提供了一种高效的对称加密解决方案,保障数据传输的安全性和完整性。本文将详细介绍此项目的核心功能、技术分析、应用场景和特点,帮助您更好地了解和利用这一优秀开源项目。 项目...

gitblog_06781的博客 846

本地玩转AI还得靠这个,SillyTavern(酒馆)到底有多强?

SillyTavern是一款本地运行的LLM交互前端,支持文本生成、图像生成、语音合成等功能。它最初是TavernAI的分支,现已独立开发,社区活跃,功能更新频繁。SillyTavern通过前端UI和插件系统连接各种大模型后端,如OpenAI、KoboldAI等,用户可以在本地创建“虚拟酒馆”与各种角色对话。其功能亮点包括角色卡、角色记忆、世界设定、多人聊天室、自定义UI和行为脚本、插件扩展支持等。SillyTavern适合AI创作用户、AI工程师、LLM爱好者和AIGC创作者。它有两个维护分支:relea

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 8028

AES-GCM 加密算法详解

AES-GCM加密算法摘要 AES-GCM是一种高效安全的加密认证算法,结合了AES对称加密和GHASH认证机制。它将AES-CTR模式与伽罗瓦域乘法结合,同时提供数据机密性和完整性保护。 核心特点: 采用CTR模式:Nonce(12字节)+Counter(4字节)生成密钥流 GHASH认证:通过伽罗瓦域乘法计算认证标签Tag 并行处理:加密和认证两条线同时进行 安全要求:同一(密钥,Nonce)对不能重复使用 工作流程: 发送方:生成密钥流加密数据 + 计算GHASH得到Tag 接收方:解密数据 + 验证

qq_41829792的博客 465

Yocto开发讲解系列 - 总目录

Yocto开发讲解系列总纲Yocto开发专栏前言

fulinux的博客 3万+

26、机器学习模型的成员推断攻击与隐私保护

本文探讨了机器学习模型面临的成员推断攻击及其对隐私的潜在威胁。通过使用CIFAR-10数据集,详细介绍了如何利用影子模型和攻击模型推断特定数据点是否属于模型的训练集。实验表明,即使无法直接访问模型参数和原始数据,攻击者仍能以高于随机猜测的概率进行推断,从而构成隐私风险。此外,文章还分析了实验的关键技术点,并提出了多种隐私保护建议,包括数据脱敏、差分隐私、模型正则化等方法。

coffee的博客 123

AES128加密算法GCM工作模式详解文档:深入理解加密技术核心

AES128加密算法GCM工作模式详解文档:深入理解加密技术核心 去发现同类优质开源项目:https://gitcode.com/ AES128加密算法GCM工作模式详解文档,该项目专注于AES128加密算法的GCM(Galois/Counter Mode)工作模式的深入解析和应用。以下是对该项目的详细介绍。 项目介绍 在现代信息安全的领域中,数据加密是保护信息不被未授权访问的关键技术之一。AES...

gitblog_06738的博客 1070

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

AES128加密算法GCM工作模式详解文档

AES128加密算法GCM工作模式详解文档 去发现同类优质开源项目:https://gitcode.com/ 本文档详细介绍了AES128加密算法的GCM(Galois/Counter Mode)工作模式。内容源自官方网站,深入浅出地阐述了GCM模式的工作原理,并提供了丰富的加密数据示例,帮助开发者验证自己的程序。需要注意的是,该文档为英文版。通过阅读本文档,您将能够: 理解AES128加密算法...

gitblog_06720的博客 370

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Fast DDS Security 默认加密算法AES--GCM介绍

Fast DDS Security AES-GCM介绍

Nemo的博客 554

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

上一篇: generic_netlink_howto
下一篇: OpenSSL 介绍和使用
废言Pro
博客等级 码龄16年 1092粉丝 142原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值