使用Linux Kernel Crypto进行加解密
通过执行该命令可以查看系统支持的密码模块
cat /proc/crypto
示例
- aes ecb 128
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
#include <string.h>
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
#define AES_KEY_SIZE 16
#define BLOCK_SIZE 16
void print_hex(const char *label, const unsigned char *data, int len) {
printf("%s: ", label);
for (int i = 0; i < len; i++)
printf("%02x", data[i]);
printf("\n");
}
int main(void) {
int opfd, tfmfd;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher",
.salg_name = "ecb(aes)"
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4)] = {0};
struct iovec iov;
unsigned char plaintext[BLOCK_SIZE] = "hello world!!!!!"; // 明文数据(16字节)
unsigned char ciphertext[BLOCK_SIZE] = {0}; // 用于存储密文
unsigned char key[AES_KEY_SIZE] = "1234567890abcdef"; // 16字节AES密钥
// 1. 创建AF_ALG套接字
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfmfd < 0) {
perror("socket");
return -1;
}
// 2. 绑定算法到套接字
if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind");
close(tfmfd);
return -1;
}
// 3. 设置AES密钥
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, AES_KEY_SIZE) < 0) {
perror("setsockopt");
close(tfmfd);
return -1;
}
// 4. 接收操作套接字
opfd = accept(tfmfd, NULL, 0);
if (opfd < 0) {
perror("accept");
close(tfmfd);
return -1;
}
// 5. 使用 ALG_SET_OP 控制消息设置为加密
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(4);
*(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;
// 6. 设置输入数据
iov.iov_base = plaintext;
iov.iov_len = BLOCK_SIZE;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// 7. 发送明文数据并读取加密结果
if (sendmsg(opfd, &msg, 0) < 0) {
perror("sendmsg");
close(opfd);
close(tfmfd);
return -1;
}
if (read(opfd, ciphertext, BLOCK_SIZE) < 0) {
perror("read");
close(opfd);
close(tfmfd);
return -1;
}
// 8. 输出明文和密文
print_hex("Plaintext", plaintext, BLOCK_SIZE);
print_hex("Ciphertext", ciphertext, BLOCK_SIZE);
// 9. 关闭套接字
close(opfd);
close(tfmfd);
return 0;
}
- aes 128 cbc
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
#include <string.h>
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
int main(void)
{
int opfd;
int tfmfd;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher",
.salg_name = "cbc(aes)"
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4) + CMSG_SPACE(20)] = {0};
char buf[16];
struct af_alg_iv *iv;
struct iovec iov;
int i;
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));
setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
"0123456789abcdef", 16);
opfd = accept(tfmfd, NULL, 0);
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(4);
*(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(20);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = 16;
memcpy(iv->iv, "0123456789abcdef", 16);
iov.iov_base = "Hello, World!!!!";
iov.iov_len = 16;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(opfd, &msg, 0);
read(opfd, buf, 16);
for (i = 0; i < 16; i++) {
printf("%02x", (unsigned char)buf[i]);
}
printf("\n");
close(opfd);
close(tfmfd);
return 0;
}
使用libkcapi
libkcapi 允许用户空间访问 Linux 内核加密 API,libkcapi 使用这个 Netlink 接口,并提供易于使用的 API,以便开发者不需要考虑低级的 Netlink 接口处理
smuellerDD/libkcapi: Linux Kernel Crypto API User Space Interface Library
[!tip]
wsl中不支持使用,在执行到
socket(AF_NETLINK, SOCK_RAW, NETLINK_CRYPTO)
时出错,errno
为93,“Address Family Not Supported by Protocol”,wsl应该是不支持SOCK_RAW
或AF_NETLINK
编译
autoreconf -i
./configure --prefix=$(pwd)/_install --enable-kcapi-test
make
make install
在代码中调用
#include "kcapi.h"
#include <stdio.h>
#include <string.h>
int main() {
struct kcapi_handle *handle;
char plaintext[] = "Hello, World!!!!";
char ciphertext[16];
char key[16] = "0123456789abcdef";
int ret;
// 打开加密算法(这里以AES-CBC为例)
ret = kcapi_cipher_init(&handle, "cbc(aes)", 0);
if (ret) {
fprintf(stderr, "Failed to initialize cipher handle\n");
return -1;
}
// 设置密钥
ret = kcapi_cipher_setkey(handle, (uint8_t *)key, sizeof(key));
if (ret) {
fprintf(stderr, "Failed to set key\n");
kcapi_cipher_destroy(handle);
return -1;
}
// 加密数据
ret = kcapi_cipher_encrypt(handle, (uint8_t *)plaintext, sizeof(plaintext),"0123456789abcdef",
(uint8_t *)ciphertext, sizeof(ciphertext), 0);
if (ret < 0) {
fprintf(stderr, "Encryption failed\n");
kcapi_cipher_destroy(handle);
return -1;
}
printf("Ciphertext: ");
for (int i = 0; i < sizeof(ciphertext); i++) {
printf("%02x ", (unsigned char)ciphertext[i]);
}
printf("\n");
// 释放资源
kcapi_cipher_destroy(handle);
return 0;
}
可以选择直接使用源文件编译,也可以选择使用动态库
gcc -o test test.c kcapi-kernel-if.c kcapi-sym.c kcapi-utils.c -g -DKCAPI_MAJVERSION=1 -DKCAPI_MINVERSION=5 -DKCAPI_PATCHLEVEL=0
或
gcc -o test test.c -lkcapi -L../_install/lib -g