当前位置:首页>Linux>Linux用户程序使用内核加密服务的方法

Linux用户程序使用内核加密服务的方法

  • 2026-09-25 19:00:19
Linux用户程序使用内核加密服务的方法

概述

在Linux用户空间,如果要进行加解密运算,通常的方法是安装类似OpenSSL的软件。但是这些加解密运算可以卸载使用内核空间的加密服务,尤其是内核适配了硬件引擎驱动,能有效降低CPU运算负载。

应用层访问内核加密服务大致有如下四种方式:

  • AF_ALG
  • libkcap
  • cryptodev-linux
  • openssl engine

下图是以OpenSSL为例,展示了用户空间到内核空间的加密服务调用路径:

Linux加密子系统分层框架

接下来我们在RISCV开发板上测试四种方法,所有的程序和库需要使用交叉编译。

AF_ALG

AF_ALG是Linux内核提供的一种特殊套接字接口,实现用户空间程序与内核加密API进行通信,为用户态提供相应的加解密服务。

应用层在使用内核加密服务之前,必须配置内核开启相应用户接口选项,如下使能哈希和对称加密的用户API配置:

CONFIG_CRYPTO_USER=yCONFIG_CRYPTO_USER_API_HASH=yCONFIG_CRYPTO_USER_API_SKCIPHER=y

编写sha256计算示例代码如下:

#include<stdio.h>#include<stdlib.h>s#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<linux/if_alg.h>#include<linux/socket.h>intmain(void){int tfmfd, opfd;// 绑定算法类型和名称structsockaddr_algsa = {        .salg_family = AF_ALG,        .salg_type = "hash",       // 算法类型:哈希        .salg_name = "sha256"// 具体算法:SHA256    };constchar *msg = "Hello World";size_t msg_len = strlen(msg);unsignedchar dgst[32];ssize_t len;// 创建AF_ALG套接字    tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);if (tfmfd < 0) {        perror("socket");exit(EXIT_FAILURE);    }// 绑定算法类型和名称if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {        perror("bind");        close(tfmfd);exit(EXIT_FAILURE);    }// 获取操作句柄    opfd = accept(tfmfd, NULL, 0);if (opfd < 0) {        perror("accept");        close(tfmfd);exit(EXIT_FAILURE);    }// 发送数据到内核进行哈希计算if (send(opfd, msg, msg_len, 0) != msg_len) {        perror("send");        close(opfd);        close(tfmfd);exit(EXIT_FAILURE);    }// 获取哈希结果    len = read(opfd, dgst, sizeof(dgst));if (len != sizeof(dgst)) {        perror("read");        close(opfd);        close(tfmfd);exit(EXIT_FAILURE);    }// 打印结果printf("Input: %s\nSHA256: ", msg);for (int i = 0; i < sizeof(dgst); i++) {printf("%02x", dgst[i]);    }printf("\n");// 清理资源    close(opfd);    close(tfmfd);return0;}

使用交叉工具链编译,并在开发板上执行,输出如下:

$ ./af_alg_sha256 Input: Hello WorldSHA256: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

默认情况下,会使用内核软件实现的方式计算哈希。如果加载了引擎驱动,就会调用硬件进行计算,但需要注意在注册该算法SHA256时,不能开启此标志CRYPTO_ALG_INTERNAL,即允许应用层调用该算法。硬件引擎驱动安装方法如下(后续其他方式想使用硬件引擎,都需要先安装驱动模块):

$ insmod crypto_engine.ko$ insmod demo_crypto.ko

同样AES-CTR-128算法的计算示例如下,需要注意明文数据长度要16字节块对齐。

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<linux/if_alg.h>#include<linux/socket.h>#ifndef SOL_ALG#define SOL_ALG 279#endifintmain(void){int tfmfd, opfd;// 绑定算法类型和名称structsockaddr_algsa = {        .salg_family = AF_ALG,        .salg_type = "skcipher",      // 算法类型:加密        .salg_name = "ctr(aes)"// 具体算法:AES-CTR    };constchar *plaintext = "Hello World!!!!!";size_t plaintext_len = strlen(plaintext);constunsignedchar key[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff    };constunsignedchar iv[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff    };structmsghdrmsg = {};structcmsghdr *cmsg;char cbuf[CMSG_SPACE(4) + CMSG_SPACE(20)] = {0};structaf_alg_iv *af_alg_iv;structioveciov;unsignedchar buf[16];// 创建AF_ALG套接字    tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);if (tfmfd < 0) {        perror("socket");exit(EXIT_FAILURE);    }// 绑定算法类型和名称if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {        perror("bind");        close(tfmfd);exit(EXIT_FAILURE);    }// 配置密钥if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)) < 0) {        perror("setsockopt");        close(tfmfd);exit(EXIT_FAILURE);    }// 获取操作句柄    opfd = accept(tfmfd, NULL, 0);if (opfd < 0) {        perror("accept");        close(tfmfd);exit(EXIT_FAILURE);    }// 配置操作和IV,并发送数据到内核进行加密计算    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);    *(unsignedint *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;// IV    cmsg = CMSG_NXTHDR(&msg, cmsg);    cmsg->cmsg_level = SOL_ALG;    cmsg->cmsg_type = ALG_SET_IV;    cmsg->cmsg_len = CMSG_LEN(20);    af_alg_iv = (struct af_alg_iv *)CMSG_DATA(cmsg);memcpy(af_alg_iv->iv, iv, 16);    af_alg_iv->ivlen = 16;// 数据    iov.iov_base = (void *)plaintext;    iov.iov_len = plaintext_len;    msg.msg_iov = &iov;    msg.msg_iovlen = 1;// 发送数据到内核进行计算if (sendmsg(opfd, &msg, 0) < 0) {        perror("sendmsg");        close(opfd);        close(tfmfd);exit(EXIT_FAILURE);    }// 获取加密结果if (read(opfd, buf, sizeof(buf)) < 0) {        perror("read");        close(opfd);        close(tfmfd);exit(EXIT_FAILURE);    }// 打印结果printf("Input: %s\nAES-CTR-128 Output: ", plaintext);for (int i = 0; i < sizeof(buf); i++) {printf("%02x", buf[i]);    }printf("\n");// 清理资源    close(opfd);    close(tfmfd);return0;}

同样使用交叉工具链编译,并在开发板上执行,输出如下:

$ ./af_alg_aes-ctr-128 Input: Hello World!!!!!AES-CTR-128 Output: 2a9315d244d08e5e167267bd82613a93

而对于非对称算法,比如rsa算法在Linux主分支中未支持通过af_alg方式调用,但是在kernel-patchs目录提供了补丁,可以自行添加到内核中。

libkcapi

库libkcapi 允许用户空间访问Linux内核crypto API。该库利用Netlink接口并封装出易于使用的API,使开发者无需关注底层Netlink接口的处理细节。该库本身不实现任何加密算法,所有消费者请求均发送至内核进行处理。内核加密API的返回结果通过相关API回传给消费者。

下载并编译,流程如下:

$ git clone  https://github.com/smuellerDD/libkcapi $ cd libkcapi$ autoreconf -i$ ./configure --prefix=/path/to/libkcapi/install --enable-kcapi-test CC=/path/to/riscv32-linux-gcc --host=riscv$ make all && make install
  • --prefix:指定库和应用的安装目录
  • --enable-kcapi-test:编译测试程序
  • CC:指定编译器
  • --host:主机平台

编译成功后库和应用程序在libkcapi/install目录下,拷贝到开发板上。

测试sha256哈希计算命令如下:

$ ./kcapi -x 3 -c "sha256" -p 48656c6c6f20576f726c64a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

测试aes-ctr加密命令如下:

$ ./kcapi -x 1 -e -c "ctr(aes)" -k 00112233445566778899aabbccddeeff  -i 00112233445566778899aabbccddeeff -p 48656c6c6f20576f726c6421212121212a9315d244d08e5e167267bd82613a93

如果运行时报如下错误libkcapi - Error: Netlink error: cannot open netlink socket,注意Linux配置需要开启CONFIG_CRYPTO_USER选项,并重新编译内核。

由于AF_ALG不支持非对称算法,如果自行安装了补丁,也可以使用libkcapi测试非对称算法,注意需要使能—enable-lib-asym选项,并重新编译。测试rsa私钥加密命令如下:

$ ./kcapi -x 4 -o 0 -c "rsa" -r 3082010902820100DB101AC2A3F1DCFF136BED44DFF0026D13C788DA706B54F1E827DCC30F996AFAC667FF1D1E3C1DC1B55F6CC0B2073A6D41E42599ACFCD20F02D3D154061A5177BDB6BFEAA75C06A95D698445D7F505BA47F01BD72B24ECCB9B1B108D81A0BEB18C33E436B843EB192A818DDE810A9948B6F6BCCD49343A8F2694E328821A7C8F599F45E85D1A4576045605A1D01B8C776DAF53FA71E267E09AFE03A985D2C9AABA2ABCF4A008F51398135DF0D933342A61C38955F0AE1A9C22EE19058D32FEEC9C84BAB7F96C3A4F07FC45EB12E57BFD55E62969D1C2E8B97859F67910C64EEB6A5EB99AC7C45B63DAA33F5E927A815ED6B0E2628F7426C20CD39A1747E68EAB0203010001 -p 54859b342c49ea2ab29776b4ae3e383c7e641fcca27ff6becf49bc48d36c8f0a0ec173bd7b5579360ea18788b92c90a6535ee9efc4e24dddf7a669823f56a47bfb62e0aeb8d304b3ac5a152ae3199b039a0b41da64ec0a69fcf21092f3c1bf847ffd2caec8b5f64170c547038af8ff6f3fd26f09b422f330bea985cb9c8df98feb3291a225848ff5dcc7069c2de5112c09098709a9f6337390f160f265dd30a566ce627bd0f82d3d198277e30a5f752f8eb1e5e891351b3b33b76692d1f28e6fe5750cad36fb4ed06661bd49fef41aa22b49fe034c74478d9a66b249464d77ea33

cryptodev-linux

cryptodev-linux 是一个/dev/crypto设备驱动,也允许用户空间调用内核的加密API。下载和编译流程如下:

# 下载源码git clone https://github.com/cryptodev-linux/cryptodev-linux# 配置交叉编译器export CROSS_COMPILE=/path/to/riscv32-linux-export CC=${CROSS_COMPILE}gccexport LD=${CROSS_COMPILE}ld# 配置架构export ARCH=riscv# 配置内核源码的编译目录export KERNEL_DIR=/path/to/linux-build# 编译$ make# 编译测试程序$ make tests

这里注意要配置内核源码的编译目录KERNEL_DIR。

编译成功会在根目录下生成cryptodev.ko。cryptode-linux也提供了一些示例,位于examples目录,这里编译哈希和加密代码,测试一下功能正确性:

$ cd examples$ {CROSS_COMPILE}gcc sha.c -o sha -I../$ {CROSS_COMPILE}gcc aes.c -o aes -I../

将以上编译的输出文件cryptodev.ko、sha和aes拷贝到设备上,执行查看哈希和加解密功能是否正常:

$ insmod cryptodev.ko$ ./shaGot sha1 with driver sha1-genericNote: This is not an accelerated cipher$ ./aesGot cbc(aes) with driver cbc(aes-generic)Note: This is not an accelerated cipherGot cbc(aes) with driver cbc(aes-generic)Note: This is not an accelerated cipherAES Test passed

可以看出功能pass,但是并未使用加速引擎。如果我们安装了硬件驱动引擎模块,就会调用硬件引擎计算。

$ insmod crypto_engine.ko$ insmod demo_crypto.ko$ ./aesGot cbc(aes) with driver cbc(aes)-acmeGot cbc(aes) with driver cbc(aes)-acmeAES Test passed

openssl engine

OpenSSL支持动态引擎,可以通过af_alg和cryptodev等方式卸载加解密请求到内核。

下载OpenSSL并进行编译,命令如下:

$ wget https://github.com/openssl/openssl/releases/download/openssl-3.4.0/openssl-3.4.0.tar.gz$ tar -xvf openssl-3.4.0.tar.gz && cd openssl-3.4.0$ ./Configure --prefix=$(pwd)/install --cross-compile-prefix=/path/to/riscv32-linux- linux32-riscv32$ make -j12 && make install
  • --prefix:指定库和应用的安装目录
  • --cross-compile-prefix:指定交叉编译器
  • linux32-riscv32:主机平台

编译成功后命令程序和动态库文件位于install目录,拷贝到目标开发板上:

$ cp openssl-3.4.0/install/lib/libcrypto.so.3 /usr/lib$ cp openssl-3.4.0/install/lib/libssl.so.3 /usr/lib$ cp openssl-3.4.0/install/bin/openssl /usr/bin

OpenSSL默认编译是支持动态引擎加载,因此执行如下命令,可以看到响应:

$ openssl engine -c(dynamic) Dynamic engine loading support

af_alg方式

首先看下af_alg方式卸载到内核,OpenSSL默认编译自带af_alg,生成的引擎so文件位于openssl-3.4.0/install/l ib64/engines-3/afalg.so。同样拷贝到目标开发板上:

$ cp openssl-3.4.0/install/lib64/engines-3/afalg.so /usr/lib

注意需要配置OPENSSL_ENGINES环境变量,否则找不到afalg.so动态库:

$ export OPENSSL_ENGINES=/usr/lib

查看引擎是否安装成功,并列举支持的算法能力,当前只支持AES-CBC算法:

$ openssl engine -c -t afalg(afalg) AFALG engine support [AES-128-CBC, AES-192-CBC, AES-256-CBC]     [ available ]

测试一下加解密,查看引擎是否正常工作:

# 纯应用层软件加密$ openssl enc -e -aes-128-cbc -K 00112233445566778899aabbccddeeff -iv 00112233445566778899aabbccddeeff -in hello.txt -out ciphertext# 启用afalg引擎加密$ openssl enc -e -aes-128-cbc -K 00112233445566778899aabbccddeeff -iv 00112233445566778899aabbccddeeff -in hello.txt -out ciphertext_afalg -engine afalgEngine "afalg"set.

我们也可以手动动态加载AF_ALG,命令如下:

$ openssl engine -t -c -pre SO_PATH:/usr/lib/afalg.so -pre ID:afalg -pre LOAD

cryptodev方式

接着看下cryptodev方式,默认OpenSSL不编译,因此需要重新编译并使能enable-devcryptoeng,另外注意需要提供cryptodev-linux的头文件目录:

$ ./Configure --prefix=$(pwd)/install --cross-compile-prefix=/path/to/riscv32-linux- linux32-riscv32 -I/path/to/cryptodev-linux enable-devcryptoeng$ make -j12 && make install

生成的引擎so文件位于openssl-3.4.0/install/l ib64/engines-3/devcrypto.so。同样拷贝到目标开发板上:

cp openssl-3.4.0/install/lib64/engines-3/devcrypto.so /usr/lib

由于openssl编译出的引擎依赖cryptodev库,因此需要先安装前一章cryptodev-linux编译出的cryptodev.ko:

$ insmod cryptodev.ko

否则直接使用devcyrpto方式,会报错:

90AF7595:error:1300006D:engine routines:dynamic_load:init failed:crypto/engine/eng_dyn.c:510:90AF7595:error:13000074:engine routines:ENGINE_by_id:no such engine:crypto/engine/eng_list.c:475:id=devcrypto

同样查看引擎是否安装成功,并列举支持的算法能力,可以看出支持AES-CBC/CTR和SHA256算法,比af_alg方式多:

$ openssl engine -c -t devcrypto(devcrypto) /dev/crypto engine [AES-128-CBC, AES-192-CBC, AES-256-CBC, AES-128-CTR, AES-192-CTR, AES-256-CTR, AES-128-ECB, AES-192-ECB, AES-256-ECB, SHA256]     [ available ]

测试一下SHA256算法,查看引擎是否正常工作:

# 纯应用层软件计算$ openssl dgst -sha256 hello.txtSHA2-256(hello.txt)= a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447# 启用devcrypto引擎计算$ openssl dgst -engine devcrypto -sha256 hello.txt Engine "devcrypto"set.SHA2-256(hello.txt)= a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447

再测试一下AES加解密:

# 纯应用层软件加密$ openssl enc -e -aes-128-cbc -K 00112233445566778899aabbccddeeff -iv 00112233445566778899aabbccddeeff -in hello.txt -out ciphertext# 启用devcrypto引擎加密$ openssl enc -e -aes-128-cbc -K 00112233445566778899aabbccddeeff -iv 00112233445566778899aabbccddeeff -in hello.txt -out ciphertext_devcrypto -engine devcryptoEngine "devcrypto"set.

最后再测试一下非对称算法RSA的签名和验签:

# 生成密钥$ openssl genrsa -out prikey.pem 2048# 提取公钥$ openssl rsa -in prikey.pem -pubout -out pubkey.pem# PEM转化为DER格式$ openssl rsa -pubin -in pubkey.pem -out pubkey.der -outform DER# 随机生成32字节哈希$ dd if=/dev/urandom of=hash bs=1 count=32# 纯应用层软件签名$ openssl pkeyutl -sign -inhash -inkey prikey.pem -out sig -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss# 纯应用层软件验签$ openssl pkeyutl -verify -inhash -sigfile sig -pubin -inkey pubkey.pem -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pssSignature Verified Successfully# 启用devcrypto引擎签名$ openssl pkeyutl -sign -inhash -inkey prikey.pem -out sig -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss -engine devcryptoEngine "devcrypto"set.# 启用devcrypto引擎验签$ openssl pkeyutl -verify -inhash -sigfile sig -pubin -inkey pubkey.pem -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss -engine devcryptoEngine "devcrypto"set.Signature Verified Successfully

参考

  1. User Space Interface
  2. OpenSSL初体验
  3. Linux内核学习笔记——Crypto基础框架

最新文章

随机文章