Libgcrypt 是一个基于代码的通用加密库在 GnuPG 中使用。 Libgcrypt 依赖于库`libgpg-error’,是一个提供加密构建块的库。下面我们快速一下了解Libgcrypt库的使用。
Libgcrypt库的所有接口
Libgcrypt库的所有接口(数据类型和函数)都在头文件 gcrypt.h 中定义。 必须直接或通过其他一些头文件将其包含在使用库的所有源文件中,如下所示:
#include <gcrypt.h>
Libgcrypt 的命名空间是 gcry_* 用于函数和类型名称
Libgcrypt 使用 libgpg-error,它使用 gpg_* 作为函数和类型名称的命名空间
使用Libgcrypt 构建源
Libgcrypt 库代码路径在这里,找个合适的版本安装。
ftp://ftp.gnupg.org/gcrypt/libgcrypt/
如果要编译包含 #include <gcrypt.h>头文件的源文件,必须确保编译器可以在目录层次结构中找到它。这是通过将头文件所在目录的路径添加到编译器包含文件搜索路径(通过 -I 选项)来实现的。
但是,包含文件的路径是在配置源时确定的。为了解决这个问题,Libgcrypt 附带了一个小的帮助程序 libgcrypt-config,它知道包含文件的路径和其他配置选项。
编译时需要添加到编译器调用中的选项由–cflags 选项输出到libgcrypt-config。以下示例显示了如何在命令行中使用它:
gcc -c foo.c `libgcrypt-config --cflags’
将‘libgcrypt-config --cflags’的输出添加到编译器的命令行将确保编译器可以找到 Libgcrypt 头文件
将程序与库链接时会出现类似的问题。同样,编译器必须找到库文件。为此,必须将库文件的路径添加到库搜索路径中(通过 -L 选项)。
可以使用选项 --libs 到 libgcrypt-config。为方便起见,此选项还输出将程序与 Libgcrypt 库链接所需的所有其他选项(特别是“-lgcrypt”选项)。该示例显示了如何将 foo.o 与 Libgcrypt 库链接到程序 foo。
gcc -o foo foo.o `libgcrypt-config --libs’
当然,也可以通过为 libgcrypt-config 指定两个选项来将两个示例组合到一个命令中:
gcc -o foo foo.c
libgcrypt-config --cflags --libs
’
Libgcrypt库的错误处理
Libgcrypt 中的许多函数如果失败就会返回错误。为此,应用程序应始终捕获错误情况并采取适当的措施,例如通过释放资源并将错误传递给调用者,或向用户显示描述性消息并取消操作。
Libgcrypt 使用 libgpg-error 库。这允许与 GnuPG 系统的其他组件共享错误代码,并将错误值从加密引擎或加密引擎的某些帮助应用程序透明地传递给用户。这样就不会丢失任何信息。
因此,Libgcrypt 不使用自己的错误代码标识符,而是使用 libgpg-error 提供的标识符。它们通常以 GPG_ERR_ 开头。
Libgcrypt库中的常见密码句柄
- gcry_cipher_open
gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *hd, int algo, int mode, unsigned int flags)
要使用密码算法,必须首先分配相应的句柄。这将使用 gcry_cipher_open 函数完成,此函数创建大多数其他密码函数所需的上下文句柄,并在hd中返回一个句柄。如果出现错误,则返回相应的错误代码。
要使用的算法的 ID 必须通过 algo 指定。要使用的密码模式必须通过 mode 指定。有关支持的密码模式和相应常量的列表:
1、流模式 (GCRY_CIPHER_MODE_STREAM) 仅适用于流密码。
2、Poly1305 AEAD 模式 (GCRY_CIPHER_MODE_POLY1305) 仅适用于 ChaCha20 流密码。
3、分组密码模式(GCRY_CIPHER_MODE_ECB、GCRY_CIPHER_MODE_CBC、GCRY_CIPHER_MODE_CFB、GCRY_CIPHER_MODE_OFB、GCRY_CIPHER_MODE_CTR 和 GCRY_CIPHER_MODE_EAX)适用于任何分组密码算法。
4、GCM 模式 (GCRY_CIPHER_MODE_CCM)、CCM 模式 (GCRY_CIPHER_MODE_GCM)、OCB 模式 (GCRY_CIPHER_MODE_OCB) 和 XTS 模式 (GCRY_CIPHER_MODE_XTS) 仅适用于块大小为 16 字节的分组密码算法。
- gcry_cipher_close
void gcry_cipher_close (gcry_cipher_hd_t h)
该函数释放由 gcry_cipher_open 创建的上下文。它还将与此密码句柄相关的所有敏感信息归零。
- gcry_cipher_setkey
为了使用句柄执行加密操作,必须首先设置密钥。
gcry_error_t gcry_cipher_setkey (gcry_cipher_hd_t h, const void *k, size_t l)
在句柄 h 表示的上下文中设置用于加密或解密的密钥 k。密钥 k 的长度 l(以字节为单位)必须与为此上下文设置的算法所需的长度相匹配,或者在具有可变密钥大小的算法的允许范围内。
- gcry_cipher_setiv
大多数加密模式需要一个初始化向量 (IV),它通常是一个充当盐值的非秘密随机字符串。
gcry_error_t gcry_cipher_setiv (gcry_cipher_hd_t h, const void *k, size_t l)
设置用于加密或解密的初始化向量。该向量作为长度为 l 字节的缓冲区 K 传递并复制到内部数据结构。该函数检查 IV 是否与所选算法和模式的要求相匹配。
- gcry_cipher_reset
gcry_error_t gcry_cipher_reset (gcry_cipher_hd_t h)
将给定句柄的上下文设置回上次调用 gcry_cipher_setkey 后的状态并清除初始化向量。
- gcry_cipher_encrypt
实际的加密和解密是通过使用以下函数之一来完成的。可以根据需要经常使用它们来处理所有数据。
gcry_error_t gcry_cipher_encrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
gcry_cipher_encrypt 用于加密数据。此功能可以就地工作,也可以使用两个缓冲区。它使用已设置并由句柄 h 描述的密码上下文。
有两种方法可以使用gcry_cipher_encrypt 该函数:如果 in 作为 NULL 传递且 inlen 为 0,则对 in-length outsize 中的数据进行就地加密。
如果 in 不是 NULL,因此 inlen 字节被加密到缓冲区 out,它必须至少具有 inlen 的大小。 outsize 必须设置为 out 的分配大小,以便函数可以检查是否有足够的空间。
这里要注意下:根据选择的算法和加密模式,缓冲区的长度必须是块大小的倍数。
- gcry_cipher_decrypt
gcry_error_t gcry_cipher_decrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
gcry_cipher_decrypt 用于解密数据。此功能可以就地工作,也可以使用两个缓冲区。它使用已设置并由句柄 h 描述的密码上下文。
有两种方法可以使用gcry_cipher_decrypt该函数:如果 in 作为 NULL 传递并且 inlen 为 0,则对数据 in out 或长度超大进行就地解密。
如果 in 不是 NULL,inlen 字节被解密到缓冲区 out,它必须至少具有 inlen 的大小。 outsize 必须设置为 out 的分配大小,以便函数可以检查是否有足够的空间。
这里要注意下:根据选择的算法和加密模式,缓冲区的长度必须是块大小的倍数。
- gcry_cipher_info
gcry_error_t gcry_cipher_info (gcry_cipher_hd_t h, int what, void *buffer, size_t *nbytes)
gcry_cipher_info 通常用于检索有关密码上下文或密码模块的各种信息。
- gcry_cipher_algo_info
gcry_error_t gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
此函数用于检索有关特定算法的信息。您将密码算法 ID 作为算法传递,并将请求的信息类型作为什么传递。
结果要么作为函数的返回码返回,要么复制到提供的缓冲区,其分配的长度必须在整数变量中可用,地址以 nbytes 传递。
- gcry_cipher_get_algo_keylen
size_t gcry_cipher_get_algo_keylen (int algo);
此函数返回算法算法的密钥长度。如果算法支持多个密钥长度,则返回支持的最大密钥长度。
- gcry_cipher_get_algo_blklen
size_t gcry_cipher_get_algo_blklen (int algo
此函数返回以八位字节计的算法算法的块长度。出错时返回 0。
- gcry_cipher_algo_name
const char * gcry_cipher_algo_name (int algo)
gcry_cipher_algo_name 返回一个带有密码算法名称的字符串。如果算法未知或发生其他错误,则字符串“?”被退回。
Libgcrypt库处理文件(加解密)
加密
/* 如果参数错误则打印用法 */
void usage_print(char * command)
{
printf("Usage: %s filename\n",command);
}
int main(int argc,char *argv[])
{
if (argc!=2)
{
usage_print(argv[0]);
return 1;
}
FILE *fin=fopen(argv[1],"rb");
FILE *fout;
gcry_cipher_hd_t cipher_hd;
gcry_error_t cipher_err;
int file_size = get_file_size(argv[1]); /* 获取文件大小 */
char *input_buf = (char*)malloc(file_size);
memset(input_buf,0,file_size);
// 纯文本缓冲区
size_t key_size = gcry_cipher_get_algo_keylen(CIPHER_ALGO); /* 算法的密钥长度 */
size_t block_size = gcry_cipher_get_algo_blklen(CIPHER_ALGO); /* 算法的块长度 */
size_t block_required = file_size/block_size;
if (file_size % block_size != 0)
{
block_required++;
}
char *cipher_buffer = (char*)malloc(block_size*block_required);
memset(cipher_buffer,0,block_size*block_required);
char *iv = (char*)malloc(block_size);
memset(iv,0,block_size);
memcpy(iv,MAGIC_STRING,sizeof(MAGIC_STRING));
char *key = get_key_from_password(); /* 从用户输入中获取密钥 */
/* open cipher */
/* 要使用密码算法,必须首先分配相应的句柄。 */
cipher_err=gcry_cipher_open(&cipher_hd,CIPHER_ALGO,
GCRY_CIPHER_MODE_CBC,GCRY_CIPHER_CBC_CTS);
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
/* set key */
cipher_err = gcry_cipher_setkey(cipher_hd,key,key_size); /* 设置用于加密或解密的密钥 */
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
/* set iv */
cipher_err = gcry_cipher_setiv(cipher_hd, iv, block_size); /* 设置用于加密或解密的初始化向量 */
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
char *outfilename = (char*) malloc(5+strlen(argv[1]));
strcpy(outfilename,argv[1]);
strcat(outfilename,".crypt");
fout = fopen(outfilename,"wb");
/* encrypt */
fread(input_buf,1,file_size,fin);
memcpy(cipher_buffer,input_buf,block_required*block_size);
/* 加密数据 */
cipher_err = gcry_cipher_encrypt(cipher_hd,cipher_buffer,
block_required*block_size,NULL,0);
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
fwrite(cipher_buffer,1,block_required*block_size,fout);
gcry_cipher_close(cipher_hd);
fclose(fin);
fclose(fout);
return 0;
}
解密
/* 如果参数错误则打印用法 */
void usage_print(char * command)
{
printf("Usage: %s filename\n",command);
}
int main(int argc,char *argv[])
{
if (argc!=2)
{
usage_print(argv[0]);
return 1;
}
FILE *fin=fopen(argv[1],"rb");
FILE *fout;
gcry_cipher_hd_t cipher_hd;
gcry_error_t cipher_err;
int file_size = get_file_size(argv[1]);
char *input_buf = (char*) malloc(file_size);
memset(input_buf,0,file_size);
// encry text buffer
size_t key_size = gcry_cipher_get_algo_keylen(CIPHER_ALGO);
size_t block_size = gcry_cipher_get_algo_blklen(CIPHER_ALGO);
char *decry_buf = (char*) malloc(file_size);
char *iv = (char*) malloc(block_size);
memset(iv,0,block_size);
memcpy(iv,MAGIC_STRING,sizeof(MAGIC_STRING));
char *key = get_key_from_password();
//open cipher
cipher_err = gcry_cipher_open(&cipher_hd,CIPHER_ALGO,
GCRY_CIPHER_MODE_CBC,GCRY_CIPHER_CBC_CTS);
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
//set key
cipher_err = gcry_cipher_setkey(cipher_hd,key,key_size);
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
//set iv
cipher_err = gcry_cipher_setiv(cipher_hd, iv, block_size);
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
char *outfilename = (char*) malloc(5+strlen(argv[1]));
strcpy(outfilename,argv[1]);
strcat(outfilename,".decrypt");
fout = fopen(outfilename,"wb");
//decrypt
fread(decry_buf,1,file_size,fin);
cipher_err = gcry_cipher_decrypt(cipher_hd,decry_buf,file_size,NULL,0);
while (decry_buf[file_size-1]==0)
{
file_size--;
}
if (cipher_err)
{
cipher_error_handler(cipher_err);
}
fwrite(decry_buf,1,file_size,fout);
gcry_cipher_close(cipher_hd);
fclose(fin);
fclose(fout);
return 0;
}
编译:
makefile
CC = gcc
CFLAGS = `libgcrypt-config --cflags` -Wall -g -std=gnu99
LIBS = `libgcrypt-config --libs`
OBJS = crypt decrypt crypt.o decrypt.o
all: $(OBJS)
crypt.o: crypt.c
$(CC) -c -o crypt.o crypt.c $(CFLAGS)
crypt: crypt.o
$(CC) -o crypt crypt.o $(CFLAGS) $(LIBS)
decrypt.o: decrypt.c
$(CC) -c -o decrypt.o decrypt.c $(CFLAGS)
decrypt: decrypt.o
$(CC) -o decrypt decrypt.o $(CFLAGS) $(LIBS)
clean:
rm $(OBJS)
运行效果:
这里我们先给test.txt文件写入hello world,以便对文件内容进行加解密。
对文件内容进行加密。在现有的文件名上追加了后缀.crypt
可以使用decrypt对文件进行解密,
总结
使用Libgcrypt 最重要的函数为:
gcry_cipher_open
创建一个新实例以使用指定的算法和模式进行加密或解密。
gcry_cipher_close
释放实例。
gcry_cipher_setkey
设置用于加密或解密的密钥。
gcry_cipher_setiv
设置用于加密或解密的初始化向量。
gcry_cipher_encrypt
gcry_cipher_decrypt
加密或解密数据。可以使用任意数量的数据调用这些函数,并且可以根据需要经常调用这些函数来加密或解密所有数据。
欢迎关注微信公众号【程序猿编码】,需要完整源码的添加本人微信号(c17865354792)