使用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_RAWAF_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
posted @ 2025-04-18 11:53  Midraos  阅读(130)  评论(0)    收藏  举报