如何传输OpenSSL AES 128 GCM标签至Intel SGX端实现Rijndael解密?
AES-128 GCM 客户端加密到SGX端解密的标签传输与实现方案
核心原理
GCM模式的解密依赖三个核心数据:密钥、初始化向量(IV)、认证标签(Tag)。其中密钥是两端预先协商好的,IV和Tag需要客户端加密后随密文一起传输到SGX端——缺少任意一项都会导致解密失败或认证不通过。
客户端(OpenSSL)加密与数据打包步骤
- 生成符合GCM标准的随机IV(推荐12字节/96位,这是NIST指定的最优长度,可避免IV重复带来的安全风险)
- 初始化AES-GCM加密上下文,设置密钥与IV
- 加密明文得到密文
- 通过
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)提取16字节的认证标签 - 将IV、密文、标签按固定结构拼接成传输包,推荐结构:
[IV(12字节)] + [密文(任意长度)] + [Tag(16字节)]
客户端代码示例
#include <openssl/evp.h> #include <stdlib.h> #include <string.h> #define AES_KEY_SIZE 16 #define GCM_IV_SIZE 12 #define GCM_TAG_SIZE 16 int aes_gcm_encrypt(const unsigned char *key, const unsigned char *plaintext, int plaintext_len, unsigned char *iv, unsigned char *ciphertext, unsigned char *tag) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int len, ciphertext_len; // 生成随机IV if (!RAND_bytes(iv, GCM_IV_SIZE)) { EVP_CIPHER_CTX_free(ctx); return -1; } // 初始化加密上下文 if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL) || !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_SIZE, NULL) || !EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_free(ctx); return -1; } // 加密明文 if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) { EVP_CIPHER_CTX_free(ctx); return -1; } ciphertext_len = len; // 完成加密并获取标签 if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { EVP_CIPHER_CTX_free(ctx); return -1; } ciphertext_len += len; if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, GCM_TAG_SIZE, tag)) { EVP_CIPHER_CTX_free(ctx); return -1; } EVP_CIPHER_CTX_free(ctx); return ciphertext_len; } // 打包传输数据:IV + 密文 + Tag unsigned char* pack_transport_data(unsigned char *iv, unsigned char *ciphertext, int ciphertext_len, unsigned char *tag, int *total_len) { *total_len = GCM_IV_SIZE + ciphertext_len + GCM_TAG_SIZE; unsigned char *buf = malloc(*total_len); if (!buf) return NULL; memcpy(buf, iv, GCM_IV_SIZE); memcpy(buf + GCM_IV_SIZE, ciphertext, ciphertext_len); memcpy(buf + GCM_IV_SIZE + ciphertext_len, tag, GCM_TAG_SIZE); return buf; }
SGX端解密步骤
- 从接收的二进制包中拆分数据:
- 前12字节提取为IV
- 中间部分为密文(总长度减去12字节IV和16字节Tag的剩余长度)
- 最后16字节提取为认证标签
- 初始化AES-GCM解密上下文,设置密钥与IV
- 解密密文
- 通过
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)传入待验证的标签 - 调用
EVP_DecryptFinal_ex完成解密——此步骤会自动验证标签有效性,验证失败则返回错误
SGX端代码示例
#include "sgx_aes_gcm.h" #include <string.h> #include <stdlib.h> #define AES_KEY_SIZE 16 #define GCM_IV_SIZE 12 #define GCM_TAG_SIZE 16 int aes_gcm_decrypt_in_sgx(const unsigned char *key, unsigned char *transport_buf, int transport_len, unsigned char *plaintext, int *plaintext_len) { if (transport_len < GCM_IV_SIZE + GCM_TAG_SIZE) { return -1; // 数据长度不足 } // 拆分IV、密文、标签 unsigned char iv[GCM_IV_SIZE]; unsigned char tag[GCM_TAG_SIZE]; int ciphertext_len = transport_len - GCM_IV_SIZE - GCM_TAG_SIZE; unsigned char *ciphertext = transport_buf + GCM_IV_SIZE; memcpy(iv, transport_buf, GCM_IV_SIZE); memcpy(tag, transport_buf + GCM_IV_SIZE + ciphertext_len, GCM_TAG_SIZE); EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int len; // 初始化解密上下文 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL) || !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_SIZE, NULL) || !EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_free(ctx); return -1; } // 解密密文 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) { EVP_CIPHER_CTX_free(ctx); return -1; } *plaintext_len = len; // 设置标签并完成解密(自动验证标签) if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, GCM_TAG_SIZE, tag) || !EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) { EVP_CIPHER_CTX_free(ctx); return -2; // 标签验证失败 } *plaintext_len += len; EVP_CIPHER_CTX_free(ctx); return 0; }
关键注意事项
- IV唯一性:每次加密必须使用全新的随机IV,绝不能重复使用同一IV和密钥组合,否则会直接泄露密钥信息
- 标签长度一致性:固定使用16字节标签,SGX端
sgx_aes_gcm.cpp默认采用此长度,避免长度不匹配导致验证失败 - 二进制传输:IV、密文、标签以原始二进制格式传输即可,无需转码为Base64(除非适配文本协议,但会增加数据体积)
- 密钥安全:密钥需通过ECDH等安全协议协商交换,绝不能明文传输
内容的提问来源于stack exchange,提问作者gsoap
相关产品推荐
相关产品推荐

