在我之前的博客中聊到了 DH 密钥交换的原理 . 这里来讨论如何使用 OPENSSL 进行 DH 密钥交换。
一 原理验证
OPENSSL 提供了一系列API, 可以在方便地进行大数计算。在这里我们先对 DH 密钥交换的原理 中讲到的原理进行代码级别的验证。对原理不感兴趣的可以跳过这一小节。
由于在 DH 密钥交换过程中,大部分的操作过程都是一样的,可以定义一个基类来抽象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
class DHBase { public: DHBase() : m_pri_key(NULL) {} virtual ~DHBase() { if (m_pri_key) BN_free(m_pri_key); }; const BIGNUM *GetSharedNum() { if (!m_pri_key) GenRandomData(); BN_CTX *ctx = BN_CTX_new(); const BIGNUM *g = DH_get0_g(m_dh); const BIGNUM *p = DH_get0_p(m_dh); BIGNUM *r = BN_new(); // r = g ^ m_pri_key % p int rst = BN_mod_exp(r, g, m_pri_key, p, ctx); assert(rst == 1); BN_CTX_free(ctx); return r; } BIGNUM *GenKey(const BIGNUM *shared_num) { if (!m_pri_key) GenRandomData(); BIGNUM *key = BN_new(); BN_CTX *ctx = BN_CTX_new(); // key = shared_num ^ m_pri_key % p int rst = BN_mod_exp(key, shared_num, m_pri_key, DH_get0_p(m_dh), ctx); assert(rst == 1); BN_CTX_free(ctx); std::cout << m_name << ": received shared num: " << GetNumString(shared_num) << std::endl; std::cout << m_name << ": generated KEY: " << GetNumString(key) << std::endl; return key; } std::string GetName() { return m_name; } static std::string GetNumString(const BIGNUM *num) { return BN_bn2hex(num); } private: void GenRandomData() { assert(m_dh); m_pri_key = BN_new(); int rst = BN_rand_range(m_pri_key, DH_get0_p(m_dh)); assert(rst == 1); while (BN_is_zero(m_pri_key)) { BN_rand_range(m_pri_key, DH_get0_p(m_dh)); } std::cout << m_name << ": generated the private num" << std::endl; } protected: DH *m_dh; std::string m_name; private: BIGNUM *m_pri_key; }; |
m_pri_key
是双方生成的私密随机数。
GetSharedNum()
返回双方交换的中间变量 G^A mod P
GenKey()
即完成了密钥交换,返回交换成功后的密钥。
继续阅读
在我之前的一篇博客中 RSA 公钥加密原理 中, 对 RSA 非对称加密原理做了简单的阐述。这篇博客主要聊如何使用 OPENSSL 进行密钥对的生成,以及非对称加解密。
一. 生成密钥对
在 OPENSSL 中, RSA
是一个很重要的结构体。它的定义在 rsa_locl.h
中,面包含了在原理中提到的所有重要的变量 随机质数 p
, q
, 公钥指数 e
, 私钥指数 d
, 以及模数 n
|
struct rsa_st { // ... BIGNUM *n; BIGNUM *e; BIGNUM *d; BIGNUM *p; BIGNUM *q; // ... }; |
生成密钥函数:
|
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); |
bits
密钥的规模(modulus
)。小于 1028 位的密钥是不安全的,小于 512 则会返回 0
e
公开的指数。它应该是一个奇数(odd number), 一般是 3, 17
或 65537
cb
生成大随机数的回调函数。一般使用 NULL 即可, 默认为 BN_GENCB_call()
继续阅读
在我之前的一篇博客里介绍了 对称加密的模式 . 这里主要聊一聊如何使用 openssl
来进行 AES 加密
.
一. OPENSSL crypto API
openssl 加密 API 分两个部分: High Level
and Low Level
. 对于大部分人来说,使用 High Level 就够用了, 这些 API 被冠以 EVP
(Envelope) ,表示对 Low Level 的封装。High Level API 提供了包括 对称/非对称加解密
, 签名
, 验证
, 哈希
,MAC
等一系列组件,屏蔽了 Low Level API 的复杂逻辑,使用起来安全高效。对于除非有需要进行加密算法级别的改进,否则不建议使用 Low Level API.
大部分 EVP API 有一个 int 型返回值 ,用来表示操作是否成功:1 表示成功, 0 表示失败。但有些时候也会返回 -1 ,表达如内存分配或者其他什么错误。官方指导代码如下:
|
if(1 != EVP_xxx()) goto err; if(1 != EVP_yyy()) goto err; /* ... do some stuff ... */ err: ERR_print_errors_fp(stderr); |
二. 使用 EVP API 进行 AES 加解密
对于 AES 加解密,EVP API 分为两种,EVP_Encrypt / EVP_Decryp
系列 和 EVP_Cipher
系列。后者是对前者进一步的封装。它们具体使用的套路都是一样的:
- 创建加解密上下文
EVP_CIPHER_CTX
- 调用
xxxInit()
函数,使用 key(密钥)、iv(前置向量)
和 cipher(算法)
对上下文初始化
- 调用
xxxUpdate()
函数进行加解密。该函数支持流式操作。即对于一段明文来说,分成多组按顺序进行加密,和一次性全部加密,不影响其生成的密文的正确性。
- 调用
xxxFinal()
函数获取上下文中遗留的信息
- 释放上下文, 完成加解密。
继续阅读
昨天为我的rmbp配了一个外接显示器,今天突然发现mac无法连接wifi了,整个下午尝试了包括重启路由器、重置路由器、更换各种wifi参数、重启电脑等等一系列操作,均无法解决。
偶然在网上发现有人说拔掉 U 盘/ type-c 设备 / 移动硬盘等外设可以解决这个问题,我尝试断开外接显示器,果然一下子连上了wifi。如果wifi和显示器只能二选一,那我的显示器岂不是白买了,在 google 上查找度尝试,最终找到几种对我有效的方法:
一 删除设置缓存
1. 关闭wifi
2. 重启电脑
以确保所有用到网络的应用程序都关闭了
3. 删除相关文件
需要先备份 /Library/Preferences/SystemConfiguration
下所有的文件,再将该文件夹删除
4. 重启电脑
5. 打开wifi
这种方法不是对每个人都有效。另外操作前最好先备份一下重要资料。
二、改变外设位置
第一种方法并不是每次都管用。偶尔还得重新弄一回。后来发现:
当外设处于 Mac 的正左侧或正右侧是,断网现在最为明显。当外设位于 Mac 的前侧或后侧时,网速最好。
三、Mac 外壳接地
无意中发现手触到 Mac 某些地方时,连网突然恢复,可能是人体接地使 Mac 天线发生了变化。多次实验当手接触到这两个位置时,信号最好:
想办法让这两个地方接地就可
和对称加密不同,公钥加密(非对称加密)的密钥分为加密密钥(公钥)和解密密钥(私钥)。 公钥是公开的,任何人都有可能知道公钥,并用公钥生成密文,而私钥是保密的,只有解密者才能知道私钥,用它来解密密文获得明文。在这里对使用最广泛的公钥密码算法-- RSA.
RSA 是发明此算法的三位科学家的姓氏的首字母(吐槽一下:外国人的命名真随意)
一. RSA 加密
在RSA中,明文,密钥和密文都是数字, RSA的加密过程可以用如下公式表达:
$$密文 = 明文^E \mod N$$
即: RSA 的密文是明文的 $E$ 次方求模 $N$ 的结果, 或者说,密文是明文的 $E$ 次方除以 $N$ 的余数。这就是整个加密过程,它非常简洁。因此,如果知道了 $E$ 和 $N$ ,那么任何人都可以进行加密运算,也就是说,
$$ E 和 N 是公钥$$
在实际使用中, E 和 N 是经精心计算的数字。
二. RSA 解密
RSA 解密公式如下:
$$ 明文= 密文^D \mod N$$
即,对密文的 $D$ 次方求模 $N$ 运算,就可以得到明文。这里是
$$D 和 N 是私钥 $$
继续阅读
在使用对称加密中,有一些无法避免的问题:
- 密钥如何从加密方传递给解密方。窃听者如果可以劫获密文,那么也可能劫获密钥。
- 如果窃听者破解了密钥,加密方如何将新的密钥安全地交给解密方
1. DH 密钥交换
解决上述问题的一种方法就是 DH 密钥交换
技术 。
DH 密钥交换全称为 Diffie-Hellman 密钥交换
,是1976年由 Whitfiedl Diffle 和 Martin Hellman 共同发明的一种算法。使用这种方法,通信双方可以在窃听者眼皮子底下来安全地传输密码。
DH 交换的步骤如下:
继续阅读
在使用 Win10 开发近 5 年的时间里,我填了各种各样的坑。最近终于受不了,将办公室里的开发机器的操作系统换回 Win7 。但是在重装 Win7 系统后,发现了一个问题:Win7 系统盘符并不是 C:/
而是 F:/
。这导致有些软件不能使用了。在网上找了一通后,找到如下办法可以将系统盘符换回 C:/
:
修改 HKEY_LOCAL_MACHINE\SYSTEM\MountedDevice\DosDevices\F:
的键名为 \DosDevices\C:
重启以后,发现系统盘符果然改成了 C:/
。然而,系统无法正常使用:无法加载用户配置文件、无法打开系统应用,甚至连资源管理器都无法打开!
这个时候如果想将注册表改回去,发现
无法打开 C:/windows/regedit.exe, 找不到指定文件
经过一通谷狗之后,有如下解决办法:
1. 恢复到之前的盘符
使用 PE 引导盘,进行系统后打开 regedit.exe
,但是这个时候修改HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices
是无效的,因为此时的注册表是 PE 系统的注册表,而不是 Win7 的注册表。应该想办法加载 Win7 的注册表,方法如下:
将鼠标定位在 HKEY_LOCAL_MACHINE\
上,选择菜单 文件 -> 加载配置单元
, 在对话框中选择 F:\Windows\System32\config\SYSTEM
,在新加载的注册表项里找到 MountedDevices
, 将 \DosDevices\C:
改加 \DosDevices\F:
,并重启。
因为 regedit.exe
只是注册表编辑器,真正的注册表文件存储在 F:\Windows\System32\config\SYSTEM
里。
2. 解决盘符的问题
在注册表里搜一下 F:/
,发现实在太多了。这决定里通过修改盘符不能解决问题而只会引发更多的问题。冷静下来思考了一下,想到如果使用磁盘映射可能可以解决问题:
首先,查看是否有磁盘占用了 盘符 C,如果有,则改之。
然后,cmd 下使用 subst
命令:
此时发现 "我的电脑" 中果然出现了 C 盘且和 F 盘一毛一样。至此问题得到解决。
3. 改进
这种方法的缺点就是,该命令在重启后会失效。所以需要在启动时自动运行。我选择将其加入到 Windows 计划任务库里
附:subst 命令
- 命令基本格式
> subst [盘符] [路径]
- 查看所有映射
> subst
- 解除某个映射
> subst [盘符] /D
超文本传输安全协议 (HTTPS, Hypertext Transfer Protocol Secure)
常称为HTTP over TLS,HTTP over SSL或HTTP Secure, 是一种使用计算器网络进行安全通信的传输协议。HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。这个协议由网景公司(Netscape)在1994年首次提出,随后扩展到互联网上。
传输层安全性协议 (TLS, Transport Layer Security)
,及其前身 安全套接层(SSL,Secure Sockets Layer)
是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障。网景公司推出HTTPS协议时,以SSL进行加密,这是SSL的起源。IETF将SSL进行标准化,1999年公布第一版TLS标准文件。随后又公布RFC 5246 (2008年8月)与 RFC 6176 (2011年3月)。TLS/SSL 不仅为浏览器提供支持,在邮箱、即时通信、VoIP、网络传真等应用程序中也得到广泛应用。
由此可知,HTTPS 是在 SSL/TLS 之上承载 HTTP 。与HTTP 不同,HTTPS 使用 https://
做为URL前缀, 默认端口为 443
.
为方便,后面将 SSL/TLS 简写为 TLS.
协议的层次
TLS 协议为由 TLS记录协议(TLS record protocol)
与 TLS握手协议(TLS handshake protocol)
这两层协议叠加而成。记录协议负责进行加密,握手协议负责进行加密之外的其它操作。
- TLS记录协议 位于握手协议的下层,负责对消息进行加密。它使用了对称加密和消息认证,但具体的算法和共享密钥则是通过握手协议在服务端和客户端之间协商决定的。
- TLS握手协议 分为4个子协议
- 握手协议 负责在客户端与服务端之间协商决定密码算法和共享密钥。基于证书的认证操作也是在这个协议中完成。在握手协商一致后,双方会互发信号来切换密码。
- 密码规格变更协议 负责向通信对象传达密码变更的信号。
- 警告协议 负责在发生错误时将错误信息传递给对方。如果没有发生错误,接下来会使用应用数据协议进行通信。
- 应用数据协议 负责将TLS 上面承载的应用数据传达给通信对象
继续阅读