Jose JSON 对象的加密与签名
使用 openssl 验证证书
证书可以具有层次结构,认证机构的公钥可以由其它认证机构施加数字签名,生成认证机构的公钥证书,一个认证机构的公钥来验证另一个认证机构的公钥,会最终形成一个认证链,认证链的终点称为根CA (Root CA). 根CA机构为自己的公钥进行数字签名,称为自签名(self-signature)
使用 openssl 生成两层结构的 CA
1 |
|
具体命令
- 生成 Root CA 自签名
1 | # req 表示 csr 证书签名请求 |
- 生成 Sub CA 的密钥,CSR 文件,以及通过 Root CA 为其颁发证书
1 | # 生成 Sub CA 的密钥,CSR 文件 |
上述的 -extfile
指定了 x509 扩展文件(extentisons) ,其中指明了颁发的证书作为 CA. 可以通过查看证书的内容看到:
1 | $ openssl x509 -in sub_ca.crt -noout -text -purpose -ext basicConstraints |
最早进行测试的时候,没有指定该扩展,通过 sub CA 的私钥对 Server 进行签发证书后,使用 openssl verify
会验证失败,所以这里猜想,应该是没有指定 CA:TRUE
的扩展,所以验证的时候,不会将 sub CA 作为一个 CA 对待。可以通过 openssl x509v3_config 查看更详细的内容。serverfault 中的该问题 openssl invalid CA certificate 应该和这里是一样的问题
- 生成 Server 的密钥,CSR 文件,并通过 sub CA 私钥为其颁发证书
1 | $ openssl req -nodes -newkey rsa:2048 -keyout server_key.pem -out server_ca.csr |
证书验证
1 | # 根证书验证(自签名) |
1 | # sub CA 证书验证 |
1 | # server 证书验证 |
安装证书
Linux: Installed CA certificate as trusted certificate
1 | sudo mkdir /usr/share/ca-certificates/extra |
*使用空格选中要安装的证书 root_ca.crt
,回车,就可以安装,安装完后,再对 root_ca.crt
sub_ca.crt
进行验证,就不要再指定 -CAfile
了,因为已经将根证书安装在系统中称为信任的证书
参考文献
[1] openSSL certificate-verification on Linux
[2] openssl invalid CA certificate
[3] openssl man x509
[5] verify-a-certificate-chain-using-openssl-verify
[6] Ubuntun 的证书配置
[7] 如何制作 CSR 文件
[8] 验证证书链
[9] openssl 证书操作
[10] 深入剖析 RSA 密钥原理及实践
基于口令的密码
基于口令的密码(Password Based Encryption, PBE) 是一种根据口令生成密钥,并使用该密钥进行加密的方法。加密和解密使用同一个密钥(即使用对称密码算法),PKCS #5 (RFC 2898) 规范描述了其实现细节,广泛使用的密码工具都对其提供了实现,如 Java 的 javax.crypto
包,密码软件 PGP
, openssl 等。
PBE 的意义
要确保消息的机密性? —-> 使用密钥 (CEK) 进行加密
如何确保密钥的机密性?—-> 用另一个密钥 (KEY) 对密钥进行加密
如何确保另一个密钥 (KEY) 的机密性? —-> 继续使用第三个密钥?死循环 ?
使用口令和盐来生成密钥 (KEK) 吧,盐可以和加密后的密钥(CEK)一起保存在磁盘上,密钥(KEK)就可以丢掉了
口令就记在脑子里吧
如果不保存一般使用的密钥,靠人类记忆没有规律,冗长的密钥是十分困难的。
PBE 的加密与解密
* PBE 的加密过程
PBE 加密
- 生成 KEK, 使用伪随机数生成器生成 盐 (salt) ,盐就是一个随机数,用于防御字典攻击,将盐与用户输入的口令一起作为单向散列函数的输入,最终得到的散列值就是 KEK, 用来对密钥进行加密的密钥
- 利用伪随机数生成器再生成会话密钥 CEK, 用 KEK 对 CEK 进行加密,并和盐保存在安全的地方。KEK 就可以丢弃了,因为只需要盐和口令就可以重建 KEK
- 使用会话密钥对消息进行加密
PBE 解密
- 重建 KEK ,利用保存的盐和用户再次输入的口令作为散列函数的输入,得到 KEK
- 利用 KEK 解密之前加密的 CEK, 得到会话密钥 CEK
- 使用 CEK 对密文进行解密
盐的作用
盐是用来防御字典攻击的,字典攻击简单来说,就是攻击者提前准备好一组可能的口令,并计算好它们的摘要,当它们窃取加密后的会话密钥后,通过将准备好的候选摘要 KEK,尝试进行破解。主要是大量的用户使用了字典的词语来设置他们的密码,所以给了攻击者机会。当使用了盐后,KEK 的可能数量会随之增大,事先生成候选的 KEK 数量会变得很大,增加了破译的难度。
拉伸
生成 KEK 时,多次使用单向散列函数可以提高安全性,即将输出再次作为单向散列函数的输入,反复多次,一般建议最少 1000 次,目的也是增加攻击者破译的难度。这种多次迭代的方法称为拉伸 (stretching)
PKCS #5
RFC 8018 (Obsoletes 2898) Password-Based Cryptography Specification Version 2.1中详细描述了 PBE 的实现步骤。
PBKDF
PBKDF 即 Password Based Key Derivation Functions, 基于口令的密钥生成函数,仅包含密钥生成的定义,不包含加密和解密的过程。
PBKDF1
密钥生成过程中应用的是一个单向散列函数(hash function),如 MD2, MD5,SHA-1等, 与 PKCS #5 v1.5 中的定义兼容, 受限于单向散列函数输出的长度,生成密钥的长度也受到限制,只出于兼容性的需求使用。
PBKDF2
密钥过程中应用的是一个伪随机数生成函数(pseudorandom function), 如 HMAC with SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, and SHA-512/256,推荐在新的应用中使用
PBES
PBES 即 Password Based Encryption Schemes, 即 PBE 的策略,其实就是 PBKDF 与加密和解密过程相结合,完成密钥的生成,对消息的加密和解密。
PBES1 PBKDF1 与对称分组密码的结合
PBES2 PBKDF2 与对称分组密码的结合
How to encrypt user passwords
jasypt-How to encrypt user passwords
使用 openssl 与 JCA 进行实践
使用 openssl
1 | guo@DESKTOP-4L69AND:/mnt/e/learning-dir/shell-learning$ echo -n 'hello' | openssl enc -aes-128-cbc -e -base64 -pbkdf2 -iter 1000 -S 3631C9DA382CE487 -p |
同样使用 JCA 验证
1 | package org.learn.something.security.crypto; |
1 | algorithm: PBKDF2WithHmacSHA256, key(hex format): 1004087f17c38d06c8b24ca69175984c |
参考阅读
[1] 图解密码技术
[2] PKCS #5 rfc8018
[3] jasypt-How to encrypt user passwords
[5] SecretKeyFactory Algorithms
[6] 廖雪峰-口令加密算法
ASN.1
简单介绍
ASN.1 (Abstract Syntax Notation dot one) ,用来描述通信协议中传输的数据,定义了抽象数据类型规范的形式, 具有健壮性, 准确性, 独立于编程语言,独立于硬件和操作系统, 可以通过简单类型组成复杂的数据类型。
例如定义联系人 Contact 类型的语法
1 | Contact ::= SEQUENCE { |
上述的例子中定义了一个联系人的结构, 具有这样结构的联系人 name = ‘Josn Smith’, phone = 987 6543210可以通过 AS1.1 的编码规则 (后面会讲到) 序列化为二进制或文本格式, 便于进行传输
1 | JSON Encoding Rules (JER) |
完整定义的示例
购买订单类型的 ASN.1 定义
1 | -- 所有的定义都需要包含在一个模块(module)中, module 是以 'BEGIN' 关键字开始, 'END' 关键字结束 |
ASN.1 基础数据类型
1 | -- BOOLEAN |
Constaints 约束
ASN.1 的 schema 定义中可以包含约束, 用来限制给定字段或类型的有效值集合, 通过使用约束, 也可以生成一个更紧凑的编码, 例如 INTEGER(0..200)
的值用一个字节进行编码就够了
基础约束
字母表约束
1 | ardToReadChars ::= IA5String (FROM("8BI10OD5S")) |
模式(类似于正则表达式)
1 | LicensePlate ::= IA5String (PATTERN "[0-9]#4(-[A-Z]#2)?") -- NNNN[-NN] |
值的大小,长度约束
1 | LicensePlate ::= IA5String (SIZE (4..7)) |
值取值范围约束
1 | CarSpeed ::= INTEGER (0..200) |
单值约束
1 | WarningColors ::= UTF8String ("Red" | "Yellow") |
包含子类型
1 | SignColors ::= UTF8String (InfoColors UNION WarningColors) |
编码约束
1 | PerInside ::= OCTET STRING ( |
更复杂的字段条件依赖约束
1 |
|
Encoding Rules - 编码规则
编码规则描述了应该将 ASN.1 中定义的值如何进行编码以进行传输(被转换为字节进行传输,或者反之,将字节转为 ASN.1 中的值) ,并且不受机器,编程语言,应用程序表示的影响。每一种编码规则都有其特点,例如在压缩方面的,解码速度方面的。任何一种编码规则都能表示要进行交换的信息,只是使用场景不同
BER, DER, CER
BER (Basic Encoding Rules) 是最古老的编码规则,使用 TLV (Tag-Length-Value) 的格式对所有的信息进行编码,即先发送一个标签(Tag)表明数据的类型,如 SEQUENCE,接着是数据的长度(Length), 然年后是实际的数据,DER (Distinguished Encoding Rules) 以及 CER (Canonical Encoding Rules) 是 BER 的子集,消除了 BER 提供的一些灵活性。DER 通常使用在于安全相关的应用中,例如 X.509 数字证书。
OER
OER (Octet Encoding Rules) - 最快的 ASN.1 编码规则,与 PER (Packed Encoding Rules) 一样,通过利用 ASN.1 的 schema 的信息表示的优势来限制包含类每个编码消息的信息数量,生成紧凑的编码输出。聚焦于编码与解码的速度。
PER
PER (Packed Encoding Rules) 最紧凑的编码规则,与 BER 不同的是,PER 不会发送 TLV 中的标签 T, 是应为消息中的组件的顺序是一致的,如果 TLV 中的值 V 是大小固定的,也不会发送长度 L. PER 同样使用的 ASN.1 定义之外的的信息来减少冗余的数据(V). 因此,PER 编码的消息更加紧凑,能够节省带宽.
XER,E-XER
XER (XML Encodind Rules), E-XER(Extended XER) 使用 XML 文本格式来进行编码, 格式为 <start-tag> value <end-tag>
. XER 与 E-XER 的区别在于 E-XER 生成的默认编码更适合与 XSD(XML Schema Definition) 引擎进行信息交换. 相同的 ASN.1 数据可以使用多个编码规则, 这意味着来自于手机的 PER 编码的消息可以转换为 E-XER 编码, 用于在一个 web 浏览器中进行展示和操作
JER
JER (JSON Encoding Rules), 基于 JSON 格式的编码规则, 相比 XER, E-XER, 更加紧凑, 易于使用. 基于 RFC-7159 中的 JSON 规范.
通过JER,ASN.1用户可以轻松调试ASN.1协议并进行故障排除.
参考文献
[1] What is ASN1
[2] ASN.1简介及OpenSSL中ASN.1接口使用举例
[3] openssl 密钥生成和解析