Libsodium 是一个用C语言编写的库,是一种新的易于使用的高速软件库,用于网络通信、加密、解密、签名等实现和简化密码学。

完成 Libsodium 安装

Libsodium 是一个用于加密,解密,数字签名,密码哈希,等的,现代的,易用的密码学库。如果你在环境中没有安装sodium。可以执行如下操作:

yum -y install libsodium libsodium-devel


Libsodium 使用方法

在程序中,只需包含头文件 sodium.h 即可。库的名字是 sodium (使用 -lsodium 来链接)。


sodium_init() 初始化 libsodium 库,必须在 libsodium 库的其他函数之前被调用。也可以自己实现sodium_init。也就是在 Unix /Linux系统中, 会打开 /dev/urandom 文件,并且会保持这个文件 fd 打开.

void randombytes(uint8_t buffer[], unsigned long long size)
{int fd;fd = open( "/dev/urandom", O_RDONLY );if( fd < 0 ) {fprintf( stderr, "Failed to open /dev/urandom\n" );exit(1);}int rc;if( (rc = read( fd, buffer, size )) >= 0 ) {close( fd );}
}

一些基础知识

  • 什么是密钥

一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算法。

  • 什么是对称加密算法

加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。

  • 什么是非对称加密算法

加密使用的密钥和解密使用的密钥是不同的。 公钥密码体制就是一种非对称加密算法。

  • 如何理解公钥密码体制

主要分为三个部分:公钥、私钥、加密/解密算法。

可以理解为,加密解密过程如下:

加密:通过加密算法和公钥对内容(或者说明文)进行加密,得到密文。
解密:通过解密算法和私钥对密文进行解密,得到明文。

公钥密码体制的公钥和算法都是公开的,私钥是保密的。在实际的使用中,有需要的人会生成一对公钥和私钥,把公钥发布出去给别人使用,自己保留私钥。

  • 简解签名和验签

服务器有两把钥匙一把公钥、一把私钥。客户端想和服务器达到秘密交流就需要服务器特有的公钥进行加密这样别人就不知道你发送的是什么,服务器收到客户端的信息后用特有的私钥解密就能看到信息内容。服务器要给客户端返回消息,这时候就会使用Hash函数对信息生成一个摘要(Digest),用私钥对摘要加密生成数字签名(Signature),将消息和数字签名一起返回给客户端。客户端收到返回后,先使用公钥对数字签名解密得到摘要,再将信息通过Hash函数生成摘要,只要这两个摘要相同则证明该信息是中途没有被篡改过的。

值得注意的是,这样也容易出现信息泄漏。因为数字签名一被更换也就不能确定是否为真实的信息发起者发出的信息了。所以为了证明服务器的数字签名是否为真实的,服务器需要将公钥进行认证,会有一个数字证书,这个证书证明了这个公钥就是这台服务器独有的。服务器返回消息的时候会将数字证书一起发送过来,客户端接收到后就可以使用证书中心声明的公钥去解开数字证书来得到这台服务器的公钥。就能判断是否为真实的公钥了。然后客户端就可以使用证书中的服务器公钥,对信息进行加密,然后与服务器交换加密信息。

Libsodium 一些常用API

 int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);

crypto_sign_keypair()函数随机生成密钥和相应的公钥。公钥放入pk(crypto_sign_PUBLICKEYBYTES字节),密钥放入sk(crypt_sign_SECRETKEYBYTES)。

int crypto_box_keypair(unsigned char *pk, unsigned char *sk);

crypto_box_keypair()函数随机生成密钥和相应的公钥。公钥放入pk(crypto_box_PUBLICKEYBYTES字节),密钥放入sk(crypt_box_SECRETKEYBYTES)。

libsodium 的设计尽可能地隐藏了加密算法的实现细节,从它的函数名就可以看出来。例如包含了 X25519 密钥交换和 ChaCha20Poly1305 认证加密的相关函数统一称为 crypto_box_*

int crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n)

从私钥生成公钥需要用到 crypto_scalarmult_curve25519_base, curve25519 系列算法可以使用任意的 32 字节作为私钥,所以可以从密码学安全的伪随机数生成器自行生成私钥。

int crypto_sign(unsigned char *sm, unsigned long long *smlen_p,const unsigned char *m, unsigned long long mlen,const unsigned char *sk);

crypto_sign()函数使用密钥sk为长度为mlen字节的消息m添加签名。签名的消息(包括签名和未更改的消息副本)放入sm中,长度为crypto_sign_BYTES+mlen字节。

int crypto_sign_open(unsigned char *m, unsigned long long *mlen_p,const unsigned char *sm, unsigned long long smlen,const unsigned char *pk);

crypto_sign_open()函数检查长度为smlen字节的签名消息sm是否具有公钥pk的有效签名。如果签名无效,则函数返回-1。

Libsodium 测试例子

一个关于如何使用libnaid加密库的简单示例。这个例子也适用于几乎相同的libnacl库——只需更改include即可。

  • 加密/解密

...void randombytes(uint8_t buffer[], unsigned long long size)
{int fd;fd = open( "/dev/urandom", O_RDONLY );if( fd < 0 ) {fprintf( stderr, "Failed to open /dev/urandom\n" );exit(1);}int rc;if( (rc = read( fd, buffer, size )) >= 0 ) {close( fd );}
}char* to_hex( char hex[], const uint8_t bin[], size_t length )
{int i;uint8_t *p0 = (uint8_t *)bin;char *p1 = hex;for( i = 0; i < length; i++ ) {snprintf( p1, 3, "%02x", *p0 );p0 += 1;p1 += 2;}return hex;
}int is_zero( const uint8_t *data, int len )
{int i;int rc;rc = 0;for(i = 0; i < len; ++i) {rc |= data[i];}return rc;
}...User *new_user(char* name)
{User* user;user = (User*) malloc(sizeof(User));user->name = name;crypto_box_keypair(user->public_key, user->secret_key);return user;
}void print_user(User *user)
{char phexbuf[2*crypto_box_PUBLICKEYBYTES+1];char shexbuf[2*crypto_box_SECRETKEYBYTES+1];printf("username: %s\n", user->name);printf("public key: %s\n", to_hex(phexbuf, user->public_key, crypto_box_PUBLICKEYBYTES ));printf("secret key: %s\n\n", to_hex(shexbuf, user->secret_key, crypto_box_SECRETKEYBYTES ));
}int main( int argc, char **argv )
{...rc = encrypt(encrypted, bob->public_key, eve->secret_key, nonce, msg, strlen(msg));if( rc < 0 ) {return 1;}printf("encrypted: %s\n", to_hex(hexbuf, encrypted, rc ));uint8_t decrypted[1000];rc = decrypt(decrypted, eve->public_key, bob->secret_key, nonce, encrypted, rc);if( rc < 0 ) {return 1;}decrypted[rc] = '\0';printf("decrypted: %s\n", decrypted);return 0;
}

编译运行:

  • 签名/验签
...int sign(uint8_t sm[], const uint8_t m[], const int mlen, const uint8_t sk[]) {unsigned long long smlen;if( crypto_sign(sm,&smlen, m, mlen, sk) == 0) {return smlen;} else {return -1;}
}int verify(uint8_t m[], const uint8_t sm[], const int smlen, const uint8_t pk[]) {unsigned long long mlen;if( crypto_sign_open(m, &mlen, sm, smlen, pk) == 0) {return mlen;} else {return -1;}
}int main(int argc, char **argv[])
{...int rc = crypto_sign_keypair(pk, sk);if(rc < 0) {return 1;}int smlen = sign(sm, m, mlen, sk);if(smlen < 0) {return 1;}mlen = verify(m, sm, smlen, pk);if(mlen < 0) {return 1;}printf("Verified!\n");return 0;
}

编译运行:

  • 密钥的派生
...int crypto_box_recover_public_key(uint8_t secret_key[]) {uint8_t public_key[crypto_sign_PUBLICKEYBYTES];char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];crypto_scalarmult_curve25519_base( public_key, secret_key );printf("recovered public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));
}void crypto_box_example()
{uint8_t public_key[crypto_box_PUBLICKEYBYTES];uint8_t secret_key[crypto_box_SECRETKEYBYTES];char phexbuf[2*crypto_box_PUBLICKEYBYTES+1];char shexbuf[2*crypto_box_SECRETKEYBYTES+1];crypto_box_keypair(public_key, secret_key);printf("public_key: %s\n", to_hex(phexbuf, public_key, crypto_box_PUBLICKEYBYTES));printf("secret_key: %s\n", to_hex(shexbuf, secret_key,  crypto_box_SECRETKEYBYTES));crypto_box_recover_public_key(secret_key);
}int crypto_sign_recover_public_key(uint8_t secret_key[]) {uint8_t public_key[crypto_sign_PUBLICKEYBYTES];char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];memcpy(public_key, secret_key+crypto_sign_PUBLICKEYBYTES, crypto_sign_PUBLICKEYBYTES);printf("recovered public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));
}void crypto_sign_example()
{uint8_t public_key[crypto_sign_PUBLICKEYBYTES];uint8_t secret_key[crypto_sign_SECRETKEYBYTES];char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];char shexbuf[2*crypto_sign_SECRETKEYBYTES+1];crypto_sign_keypair(public_key, secret_key);printf("public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));printf("secret_key: %s\n", to_hex(shexbuf, secret_key,  crypto_sign_SECRETKEYBYTES));crypto_sign_recover_public_key(secret_key);
}int main( int argc, char **argv )
{printf("\ncrypto_sign_example:\n");crypto_sign_example();printf("\ncrypto_box_example:\n");crypto_box_example();
}

编译运行

实践:发送/接收可验证的单个加密消息

gen_keypair将创建单个密钥对 。需要两个密钥对才能发送经过验证的消息。

gen_keypair:

...int main(int argc, char *argv[])
{...if(argc != 2) {fprintf(stderr, "%s: <NAME>\n", argv[0]);return 1;}/* Generate Key Pair */crypto_box_keypair(&public_key[0], &secret_key[0]);snprintf(filename, sizeof(filename), "%s.pub", argv[1]);err = savefile(filename, &public_key[0], crypto_box_PUBLICKEYBYTES);if(err) {printf("ERROR saving public key: %s\n", filename);} else {printf("Saved public key: %s\n", filename);}snprintf(filename, sizeof(filename), "%s.key", argv[1]);err = savefile(filename, &secret_key[0], crypto_box_SECRETKEYBYTES);if(err) {printf("ERROR saving secret key: %s\n", filename);} else {printf("Saved secret key: %s\n", filename);}return 0;
}

编译运行:

ip_receptor:

...int main(int argc, char *argv[])
{...parse_args(argc, argv);g_publickey = (unsigned char *)get_file(g_pfilename);if(!g_publickey) {fprintf(stderr, "get_file(%s) failed!\n", g_pfilename);exit(EXIT_FAILURE);}g_secretkey = (unsigned char *)get_file(g_sfilename);if(!g_secretkey) {fprintf(stderr, "get_file(%s) failed!\n", g_sfilename);exit(EXIT_FAILURE);}ph = pcap_open_live(g_dev, 65535, 1, 1, pcap_errbuf);if(!ph) {fprintf(stderr, "pcap_open_live(%s, 65535, 1, 1): %s\n", g_dev, pcap_errbuf);return 2;}...printf("Starting capture on %s (%s)\n", (g_dev ? g_dev : "all"), "LINKTYPE");printf("Looking for IP Protocol %u\n", g_proto);...pcap_close(ph);...printf("Packet Count: %lu\n", g_pktcount);return 0;
}struct options opts[] =
{{ 1, "proto",    "IP Protocol to listen for",  "p",  1 },{ 2, "dev",       "Interface to listen on",     "i",  1 },{ 3, "public",    "Public Key",                 NULL, 1 },{ 4, "secret",  "Secret Key",                 NULL, 1 },{ 0, NULL,        NULL,                           NULL,   0 }
};static void parse_args(int argc, char **argv)
{char *args;int c;while ((c = getopts(argc, argv, opts, &args)) != 0) {switch(c) {case -2:fprintf(stderr, "Unknown Getopts Option: %s\n", args);break;case -1:fprintf(stderr, "Unable to allocate memory for getopts().\n");exit(EXIT_FAILURE);break;case 1:g_proto = atoi(args);break;case 2:g_dev = strdup(args);break;case 3:g_pfilename = strdup(args);break;case 4:g_sfilename = strdup(args);break;default:fprintf(stderr, "Unexpected getopts Error! (%d)\n", c);break;}free(args);}if(g_proto > 255) {fprintf(stderr, "I need a protocol! (Fix with -p)\n");exit(EXIT_FAILURE);}if(!g_pfilename) {fprintf(stderr, "I need a Public Key! (Fix with --public)\n");exit(EXIT_FAILURE);}if(!g_sfilename) {fprintf(stderr, "I need a Secret Key! (Fix with --secret)\n");exit(EXIT_FAILURE);}
}

ip_knock:

...int main(int argc, char *argv[])
{...parse_args(argc, argv);publickey = (unsigned char *)get_file(g_pfilename);if(!publickey) {fprintf(stderr, "get_file(%s) failed!\n", g_pfilename);exit(EXIT_FAILURE);}secretkey = (unsigned char *)get_file(g_sfilename);if(!secretkey) {fprintf(stderr, "get_file(%s) failed!\n", g_sfilename);exit(EXIT_FAILURE);}l = libnet_init(LIBNET_RAW4, g_dev, errbuf);if(l == NULL) {fprintf(stderr, "libnet_init() failed: %s\n", errbuf);exit(EXIT_FAILURE);}dst_ip = libnet_name2addr4(l, g_dst, LIBNET_RESOLVE);if(dst_ip == -1) {fprintf(stderr, "Bad destination IP address: %s\n", g_dst);exit(EXIT_FAILURE);}src_ip = libnet_get_ipaddr4(l);if(src_ip == -1) {fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));exit(EXIT_FAILURE);} else {printf("Using: %s\n", libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE));}...libnet_destroy(l);return 0;
}struct options opts[] =
{{ 1, "dst",      "Destination",    "d",  1 },{ 2, "proto", "Protocol",       "p",  1 },{ 3, "dev",       "Device",     "i",  1 },{ 4, "public",    "Public Key", NULL, 1 },{ 5, "secret",  "Secret Key", NULL, 1 },{ 6, "message", "Message",        "m",  1 },{ 0, NULL,      NULL,           NULL, 0 }
};...

运行结果:
ip_receptor:

ip_knock:

tcpdump抓包:

ip_knock将发送一个用ip封装的加密消息,协议值由你选择。

ip_receptor将监听具有特定ip协议值的加密消息,并使用提供的密钥对其进行认证。

验证消息后,ip_receptor将默认对消息运行系统命令。

If you need the complete source code of xxd, add your WeChat number (c17865354792)

总结

使用libsodium库头文件sodium.h 是唯一需要包含的头文件。编译时库的名字是 sodium (使用 -lsodium 来链接)。Libsodium 是一个用于加密,解密,数字签名,密码哈希,等的现代的易用的密码学库。更多了解,请参考官网文档。

Welcome to follow WeChat official account【程序猿编码

参考:
1.https://doc.libsodium.org/installation#integrity-checking
2.https://learnku.com/articles/54114
3.https://www.itcodet.com/cpp/

Linux下加密库Libsodium 使用实践(ip监听、封装的加密消息、运行系统命令)相关推荐

  1. linux oracle新建监听,linux 下 oracle图形界面(需配置监听,创建实例)

    摘要:oracle 图形界面配置监听,创建实例,以下都是我安装是的截图,按步骤一定可以安装成功! CentOS6.7_x64_minimal_desktop的虚拟机一台(安装oracle需要图形界面, ...

  2. Linux下启动/关闭Oracle服务和 oracle监听启动/关闭/查看状态

    文章目录 一.Linux下启动Oracle 二步曲 1)启动监听 2)启动数据库实例 二.Linux下启动Oracle 实战操作 2.1. 登录服务器,切换到oracle用户,或者以oracle用户登 ...

  3. Linux下 timerfd创建定时器并使用 epoll 监听

    介绍:Linux系统提供了timerfd系列的定时函数,其具体函数名如下 #include <sys/timerfd.h>int timerfd_create(int clockid, i ...

  4. 修改linux xorg端口,Xorg服务开启tcp/ip监听,允许其它机器客户端连接

    Linux在默认级别5启动时,会进入图形界面,如果不使用vnc服务,默认的显示服务器是Xorg,但由于安全因素,系统默认关闭了显示服务器Xorg监听TCP/IP地址的功能,所以使用netstat查看时 ...

  5. linux 加密库 libsodium 安装

    目录 libsodium 简介 libsodium 安装 libsodium 简介 The Sodium crypto library (libsodium)Sodium is a new, easy ...

  6. linux下编译libuv,linux下libuv库安装教程

    下载并编译libuv libuv需要自己手动下载源码,并手动编译. 当前目录为:/home/xlz/test/github/,在后面,会用$PATH来代替,我的系统的Debian8,64bit. $g ...

  7. linux编译安装jpeg,Linux下JPEG库安装脚本(转)

    Linux下JPEG库安装脚本(转)[@more@]该脚本用于在Linux下安装JPEG库,在安装GD库的时候如果没有JPEG库,GD将不能生成JPEG格式的图象. 作者: 何志强#----where ...

  8. Linux下boost库的编译、安装详解

    1.下载源文件 去官网下载:http://www.boost.org/ 这里下载最新版本 wget https://dl.bintray.com/boostorg/release/1.64.0/sou ...

  9. linux下用C语言实现TCP/IP服务器与客户端互相发送数据的socket编程

    linux下用C语言实现TCP/IP服务器与客户端互相发送数据的socket编程 server.c #include <sys/stat.h>#include <fcntl.h> ...

最新文章

  1. 菜鸟裹裹电脑版_干货|利用菜鸟裹裹商家版低价寄快递
  2. 第二阶段团队项目冲刺第六天
  3. Android Studio系列(二)使用Android Studio开发/调试整个android系统源代码(不定时更新)
  4. 计算机系统结构相关技术,计算机系统结构第1章技术总结.pptx
  5. java–jwt_java – Spring引导如何使用jwt管理用户角色
  6. 理解sklearn.feature.text中的CountVectorizer和TfidfVectorizer
  7. 蓝桥杯 ADV-154 算法提高 质数的后代
  8. AMFPHP基本安全问题
  9. 操作系统实验一:Linux基本操作
  10. Android APP漏洞自动化静态扫描检测工具-Qark
  11. No serializer found for class JSONNull and no properties discovered to create BeanSerializer
  12. 移植linux内核串口配置,uClinux内核的移植 - bootloader对uClinux的S3C44B0移植
  13. 如何使用代理ip软件的代理ip来维护你的隐私安全?
  14. [C#]使用Process的StandardInput与StandardOutput写入读取控制台数据
  15. 【课程】04 土壤水动力学
  16. 解决禅道升级报错:Column not found: 1054 Unknown column 'readme' in 'zt_im_client'
  17. 赛普拉斯PSoC6正式接入阿里云Link TEE加强物联网应用的安全设计...
  18. Jeesite Login 登录 分析
  19. yarn部署及Linux的OOM、clean机制
  20. GoF设计模式学习笔记 第一回 Mediator

热门文章

  1. 用计算机弹出不知所措怎么弹,电脑经常卡死弹出应用程没有响应解决措施
  2. 地震预测(模拟链表)
  3. 又到520了,来画一朵抽搐的玫瑰花吧
  4. 瑞士十大著名旅游景点有哪些?
  5. 「兼容M1」iZotope Neutron 3 Advanced for mac - 智能中子混音插件包
  6. 支持Unity引擎!Visbit推出Web VR播放器云服务
  7. Java数据结构与算法 二
  8. MySQL插入数据很慢优化
  9. airpodspro窃听模式_苹果 AirPods Pro 的“通透模式”和“关闭”有什么区别?
  10. CF420 div2 821A Okabe and Future Gadget Laboratory