密码学入门简明指南
这篇文章最后更新的时间在六个月之前,文章所叙述的内容可能已经失效,请谨慎参考!
这篇文章不涉及密码学的数学原理,只提及相关概念和应用。
文章的标题改成 应用密码学 可能会更好一点
相关的概念
古典密码 基于字符的替换或移项,或者两者混合使用。
现代密码 使用计算机技术的,在比特和字节上操作。
明文 = 在密码学中是指传送方想要接收方获得的可读信息
密文 = 经过加密的明文
加密 = 把明文转换为密文的过程
解密 = 把密文转换为明文的过程
密钥 = 指某个用来完成加密、解密、完整性验证等密码学应用的秘密信息
密码 约等于 密钥
Hash = 哈希,又被翻译为 离散
- 按照不同的语境,可以理解为的 哈希值(hash value) 或 哈希函数(hash function)
- 哈希值就是指一段信息经过哈希函数处理后输出的值
- 哈希函数无法逆向演算回原本的数值
- 对于大多数情况下,哈希值不一样,原本的值也不一样
- md5 和 sha-1 都存在哈希冲突的情况,就是哈希值一样但原本的值不一样
- 这是一些常见的哈希函数
- md5
- sha-1
- sha-256
- sha-512
数字摘要 = 一段信息的哈希值,一般情况下是指明文的哈希值
MD = 消息摘要 (Message Digest) ,就是数字摘要
OWF = 单向函数(One-Wayness Function) ,单向函数是一种容易计算同时难以求逆的函数。通常使用在消息摘要里。 哈希函数就是一种单向函数
OWF = 单向函数(One-Wayness Function) ,单向函数是一种容易计算同时难以求逆的函数。通常使用在消息摘要里。 哈希函数就是一种单向函数
MAC = 消息验证码 (Message Authentication Code) ,就是明文 + 密钥 + MAC 算法生成的一段数据,这里的密钥一般是对称加密的密钥,MAC 算法有很多种,但最常用的是 hash 算法,就是明文加上密钥的数字摘要
HMAC = 使用 hash 算法的 MAC
Nonce 是 Number used once 或 Number once 的缩写,数字使用一次,在密码学中 Nonce 是一个只被使用一次的任意或非重复的随机数值
时间戳 timestamp 表示的是从世界标准时间(UTC,Coordinated Universal Time)的 1970 年 1 月 1 日 0 时 0 分 0 秒开始到现在的秒数,时间戳大部分情况下是 10 位的,但有时也会用 13 位的时间戳
块加密 又称为 分块加密 或 分组加密 ,是将明文分成多个等长的模块(block),使用确定的算法和对称密钥对每组分别加密解密。块加密通常有三个参数,算法 密码强度 模式,例如 aes-256-gcm , aes 是对称加密的算法, 256 是密码强度, gcm 是分组密码的工作模式,通常只有块加密的算法有工作模式的参数。DES 和 AES 都是块加密
流加密 加密和解密双方使用相同伪随机加密数据流(pseudo-randomstream)作为密钥,明文数据每次与密钥数据流顺次对应加密,得到密文数据流。实践中数据通常是一个位(bit)并用异或(xor)操作加密。RSA 是流加密
对称加密 = 加密和解密都用同一个密钥。
非对称加密 = 加密和解密使用不同的密钥。
- 公开的密钥用于加密和验证签名,称为公钥;不公开的密钥用于解密和签名,称为私钥
- 一般是一对密钥,加密用其中一个密钥,那么解密就要用另一个密钥。一般两个密钥的长度是不一样的,一般长度较短的密钥会公开,称为公钥,长度较长的密钥不会公开,称为私钥 (这个其实只适用于 RSA ,其它非对称加密算法不是这样的)
公开密钥密码学 (Public-key cryptography) 就是 非对称加密
公钥 = 在非对称加密中,可以公开的密钥
私钥 = 在非对称加密中,不能公开的密钥
数字签名 = 在非对称加密中,由私钥和签名算法生成的信息。在 RSA 中数字签名是由私钥加密后的数字摘要,但其它非对称加密算法不是这样的。
密钥密文 一般是指,对称加密和非对称加密组合使用下,使用公钥 加密 对称加密的密钥
数字信封 一般是指,对称加密和非对称加密组合使用下,内容是 密钥密文 + 密文 (使用对称加密的密文) 的信息。数字信封既发挥了对称加密算法速度快的优点,又发挥了非对称加密算法密钥管理方便的优点。
CEK = 内容密钥 (Contents Encrypting Key) ,就是用于加密明文的密钥,是和 KEK 相对的
KEK = 加密密钥的密钥 (Key Encrypting Key)
PBE = 基于口令的密钥 (Password Based Encryption,PBE) ,在 PGP 里会用 PBE 加密私钥
盐(Salt),在密码学中,是指在散列之前将散列内容(例如:密码)的任意固定位置插入特定的字符串。这个在散列中加入字符串的方式称为“加盐”。其作用是让加盐后的散列结果和没有加盐的结果不相同,在不同的应用情景中,这个处理可以增加额外的安全性。在大部分情况,盐是不需要保密的。盐可以是随机产生的字符串,其插入的位置可以也是随意而定。
数字证书 = 用户信息(csr) + 用户公钥 + 公钥指纹 + 其它相关信息 + (颁发机构私钥 加密 前面提及的 相关数据的 数字摘要 所得到的数字签名)
- https 里的 ssl 证书就是数字证书
- 这是数字证书的一般格式
证书 版本号 序列号 由证书颁发者分配的本证书的唯一标识符。颁发者和证书序列号配合起来就能唯一地标识一张数字证书。 签名算法 颁发者 国家(C,Country) 州/省(S,State) 地域/城市(L,Location) 组织/单位(O,Organization) 通用名称(CN,Common Name) 证书有效期 此日期前无效 此日期后无效 主题 国家(C,Country) 州/省(S,State) 地域/城市(L,Location) 组织/单位(O,Organization) 通用名称(CN,Common Name) 在 TLS 应用上,此字段一般是域名 主题公钥信息 公钥算法 主题公钥 颁发者唯一身份信息(可选项 AKID , authority key identifier ) 颁发者公钥的数字摘要 主题唯一身份信息(可选项 SKID , subject key identifier ) 主题公钥的数字摘要 扩展信息(可选项) 主题别名(Subject Alternative Name) 在 TLS 应用上,如果一个证书需要支持多个域名,则其它域名会填在这里 授权密钥标识符 颁发者的公钥 ... 证书签名算法 数字签名
PKC = 公钥证书 (Public-Key Certificate,PKC) ,就是数字证书
公钥指纹 = 公钥的数字摘要,是用于标识较长公共密钥字节的短序列。由于指纹较比生成它们的密钥短得多,因此可以用来简化某些密钥的管理任务。
颁发证书 = CA 用私钥加密 CSR 和 用户公钥然后生成数字证书的过程
csr 是 cerificate signing request 的英文缩写,即证书请求文件,内容是 公钥 + 用户信息(例如 域名之类的),一般是向 CA 申请数字证书用的。 X.509 证书申请的格式标准为 PKCS10 和 rfc2314 。 cerificate 证书 signing 签署 request 请求
CA = 数字证书认证 (Certificate Authority)
CA 中心 = 数字证书认证机构,一般情况下 CA 和 CA 中心 都是指 数字证书认证机构
CA 系统 = 用于颁发数字证书的系统,由 CA 中心管理和运营
CAA = 数字证书认证授权 (Certificate Authority Authorization) , CAA 是一种 DNS 记录,它允许站点所有者指定允许哪些证书颁发机构(CA)颁发包含其域名的证书。
crt 和 cert 都是 certificate 的缩写
最终实体证书 = 普通用户向 CA 申请后使用的证书
自签证书 = 用户信息(csr) + 用户公钥 + 私钥加密前面两个数据所得到的数字签名(用户私钥)
证书链是从终端用户证书后跟着一系列的 CA 证书,而通常最后一个是自签名证书,并且有如下关系:
- 在证书链上除最后一个证书外,证书颁发者等于其后一个证书的主题。
- 除了最后一个证书,每个证书都是由其后的一个证书签名的。
- 最后的证书是信任主题,由于是通过可信过程得到的,所以可以信任。
根证书 = 证书链里最后一个证书,一般是自签证书,一般是 CA 的自签证书,根证书一般是直接内置在客户端(内置在浏览器或操作系统里)的
中间证书 = 一个 CA 向另一个 CA 颁发的证书,又或者说证书链中处于中间位置的证书,一个证书链里可以有多个中间证书
在线证书状态协议(英语:Online Certificate Status Protocol,缩写:OCSP)是一个用于获取 X.509 数字证书撤销状态的网际协议,在RFC 6960中定义,作为证书吊销列表(CRL)的替代品解
证书吊销列表(英文:Certificate revocation list,缩写:CRL,或译作证书废止清册)是尚未到期就被证书颁发机构吊销的数字证书的名单。这些在证书吊销列表中的证书不再会受到信任。目前,在线证书状态协议(OCSP)已经替代证书吊销列表(CRL)成为检查证书状态的主流。
证书链的验证
- 从证书里获取颁发者信息 -> 从仓库里获取颁发者的公钥 -> 公钥验证数字证书的数字签名
- 重复这个过程一直到根证书为止
- 这个过程中还会查询 CRL 和 OCSP ,确认证书没有过期。还可能会验证密钥用法,证书策略等属性
PKI = 公开密钥基础设施 (Public Key Infrastructure) , PKI 包含 PKIX 和 PKCS
- PKI 的核心是数字证书的发行,一般来说,构成 PKI 的主要要素就是下面三个概念
- 数字证书
- 认证机关 (CA)
- 证书库
- PKI 的核心是数字证书的发行,一般来说,构成 PKI 的主要要素就是下面三个概念
PKCS = 公钥密码学标准 (The Public-Key Cryptography Standards)
- 是由美国 RSA 数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。
X.509 = PKIX
PKIX = 公钥基础设施 X.509 (Public Key Infrastructure X.509) ,X.509 是密码学里公钥证书的格式标准,证书组成结构标准用 ASN.1 来进行描述
X.509 是密码学里公钥证书的格式标准, PKCS 是一系列密码学的标准, PKCS 有十几个标准, X.509 大致相当于 PKCS1 和 PKCS12
PKCS #12 定义了一种存档文件格式,用于实现存储许多加密对象在一个单独的文件中。通常用它来打包一个私钥及有关的 X.509 证书,或者打包信任链的全部项目。一个 PKCS #12 文件通常是被加密的,同时单独存在(存档文件格式)。
- 可以打包密钥对和数字证书
PFX = PKCS#12 ,其实也不算是严格相等,但大多数语境下可以相等
PKCS 比较常用的是 1 7 8 10 12
- 1 RSA 密码编译标准(RSA Cryptography Standard)。定义了RSA的数理基础、公/私钥格式,以及加/解密、签/验章的流程。
- 7 密码消息语法标准(Cryptographic Message Syntax Standard)。参见RFC 2315。规范了以公开密钥基础设施(PKI)所产生之签名/密文之格式。
- 8 私钥消息表示标准(Private-Key Information Syntax Standard)
- 13 椭圆曲线密码学标准(Elliptic curve cryptography Standard)
- 椭圆曲线将会取代 RSA
- 据说椭圆曲线能抵御量子计算
- 14 伪随机数产生器标准(Pseudo-random Number Generation)
ASN.1 抽象语法符号 (Abstract Syntax Notation One,ASN.1)
- ASN.1 是一种用来定义数据结构的接口描述语言
- ASN.1 有一套关联的编码规则,这些编码规则用来规定如何用二进制来表示数据结构, DER 是其中一种
DER = 可辨别编码规则 (Distinguished Encoding Rules) ,是一种二进制的用于保存证书和密钥的文件编码格式, windows 和 java 是用这种格式的
PEM = 私密 增强 邮件 (Privacy Enhanced Mail) ,是一种纯文本的用于保存证书和密钥的文件编码格式,而 DER 编码的内容是二进制的,不适合与邮件传输(早期 email 不能发送附件), nginx apahce php 是用这种格式的
JKS = Java Key Storage, JAVA 的专属格式,利用 JAVA 的一个叫 keytool 的工具可以进行格式转换。一般用于 Tomcat 服务器。
PKCS 和 X.509 是格式标准,而 DER 和 PEM 是文件编码格式
CA/浏览器论坛 (CA/Browser Forum) ,是一个证书颁发机构、网页浏览器软件供应商、操作系统,以及其他采用PKI的应用程序的自愿联合体,其颁布X.509 v.3电子证书的签发与管理的行业治理方针,在诸多应用程序中链接到信任锚。其方针涵盖用于SSL/TLS协议和代码签名的证书,以及系统和证书颁发机构的系统和网络安全。
会话密钥,加密会话内容的密钥,大多数情况下只用一次。
主密钥,会被重复使用的,加密会话密钥的密钥。
PSK = 预共享密钥 (pre-shared key)
常用的算法
- 对称加密
- des
- 3des
- aes
- 非对称加密
- 大数的因数分解
- RSA
- 离散对数
- DH
- ECC
- ElGamal
- ECDSA
- 大数的因数分解
- 数字摘要
- sha2
- md5
- 对称加密
密码套件 (cipher suite) 或者称为密码包,是 https 中各种算法的集合,一般包括 密钥交换算法 身份验证算法 加密算法(加密方法-加密强度-模式) 哈希算法 。一般情况下密码包里的算法不能重复出现,例如 密钥交换算法用了 RSA ,那么身份验证算法就不能用 RSA
PR = 伪随机数 (Pseudo Random) ,计算机里生成真正的随机数其实挺麻烦的,所以一般都是用伪随机数
PRF = 伪随机数函数 (Pseudo Random Function) ,就是用于生成伪随机数的方法
PRP = 伪随机置换 (Pseudo Random Permutation)
PRNG = 伪随机数生成器 (Pseudo Random Number Generator) ,就是用于生成伪随机数的方法
TRNG = 真随机数生成器 (True Random Number Generator)
HRNG = 硬件随机数生成器 (Hardware Random Number Generator) ,大部分语境下 HRNG == TRNG ,单靠软件无法生成真随机数
CSPRNG = 密码学安全伪随机数生成器 (Cryptographically secure pseudo-random number generator)
TTP 可信赖的第三者 (Trusted third party) ,在 PKI 里通常是指 CA
OTP 一次性密码本 (one-time pad)
HTTPS = HTTP + TLS 或 HTTP + TLS , HTTP + SSL 已经被弃用,现在的都是 HTTP + TLS ,有些时候也会称为 HTTP + TLS/SSL
WSS = WebSocket + TLS/SSL
SSL = 安全套接层 (Secure Sockets Layer) ,网景公司(Netscape)在 1994 年推出首版网页浏览器-网景导航者时,推出 HTTPS 协议,以 SSL 进行加密
TLS = 传输层安全性协议 (Transport Layer Security) , IETF 将 SSL 标准化,并将其称为 TLS , 1999 年公布 TLS 1.0 标准文件(RFC 2246)。其实大多数 TCP 的协议都可以在上面套一层 TLS
除了 HTTP 之外 TSL/TLS 可以跟很多常用的应用层协议(比如:FTP、SMTP、POP、Telnet)搭配使用
TLS 服务器证书,就是启用 HTTPS 时需要用到的数字证书,也称为 SSL 证书,一般是从 CA 申请后部署在 HTTP 服务器的
一般语境下的 SSL 证书,就是 Common Name 是域名的数字证书
- SSL 证书按照域名的数量可以分为
- 单域名证书
- 多域名证书 除了 Common Name 是域名之外,其它域名都填在 扩展信息(可选项)里的主题别名(Subject Alternative Name)
- 通配符证书 Common Name 的域名最后一级是通配符 * ,但只能匹配一级的域名
- SSL 证书按照审核的内容可以分为
- 域名验证证书 (Domain Validation Certificate DV) ,验证域名的所有权,通常是免费的,颁发速度很快,十分钟左右就能完成颁发
- 组织验证证书 (Organization Validation Certificate OV) ,验证域名所有权,也要验证企业或组织身份, 因为 CA 会验证企业或组织的身份,一般需要几天才完成颁发
- 扩展验证证书 (Extended Validation Certificate EV) ,除了验证域名所有权, CA 验证企业或组织身份之外,还会引入第三方审核,可能牵涉专业法律人员的调查及独立审计人员
- SSL 证书按照域名的数量可以分为
S/MIME ,安全的多用途 Internet 邮件扩展(Secure Multipurpose Internet Mail Extensions,简称S/MIME)是一种 Internet 标准,它在安全方面对 MIME 协议进行了扩展,可以将 MIME 实体(比如数字签名和加密信息等)封装成安全对象,为电子邮件应用增添了消息真实性、完整性和保密性服务。 S/MIME 不局限于电子邮件,也可以被其他支持 MIME 的传输机制使用,如 HTTP
证书颁发机构浏览器论坛,也称 CA/浏览器论坛(英语: CA/Browser Forum 缩写: CA/B)、证书机构与浏览器论坛,它是一个证书颁发机构、网页浏览器软件供应商、操作系统,以及其他采用 PKI 的应用程序的自愿联合体,其颁布 X.509 v.3 电子证书的签发与管理的行业治理方针,在诸多应用程序中链接到信任锚。其方针涵盖用于 SSL/TLS 协议和代码签名的证书,以及系统和证书颁发机构的系统和网络安全。
证书透明度(英语:Certificate Transparency,简称CT)也称证书透明、证书透明化,它是一个实验性的IETF开源标准和开源框架,目的是监测和审计数字证书。通过证书日志、监控和审计系统,证书透明度使网站用户和域名持有者可以识别不当或恶意签发的证书,以及识别数字证书认证机构(CA)的作为。
向前保密,前向安全性或前向保密性(英语:Forward Secrecy,缩写:FS),有时也被称为完美前向安全(英语:Perfect Forward Secrecy,缩写:PFS),是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。
零知识证明(Zero Knowledge Proof),是这样的一个过程,证明者在不向验证者提供任何额外信息的前提下,使验证者相信某个论断(Statement)是正确的。证明过程包括交互式(Interactive)和非交互式(Non-interactive)两种。
各类编码的转换,例如 base64 和 urlencode ,这类编码的转换虽然可以把一般的字符串转换成一堆看上去是乱码的字符串,但实质上并没有加密的功能,只是增加了普通人的阅读障碍。有些时候也可以用这类方式来规避一些内容的审查
数字指纹 主要用于版权保护,其研究具有重要的意义。数字指纹是将不同的标志性识别代码——指纹,利用数字水印技术嵌入到数字媒体中,然后将嵌入了指纹的数字媒体分发给用户。发行商发现盗版行为后,就能通过提取盗版产品中的指纹,确定非法复制的来源,对盗版者进行起诉,从而起到版权保护的作用。
e2ee 端到端加密 (End-to-end encryption) 是一种只有参与通讯的用户可以读取信息的通信系统。总的来说,它可以防止潜在的窃听者——包括电信供应商、互联网服务供应商,获取能够用以解密通讯的密钥。此类系统被设计为可以防止潜在的监视或篡改企图,因为没有密钥的第三方难以破译系统中传输或储存的数据。
SSH = Secure Shell
- Secure Shell Protocol (rcf 4251) 由 IETF 的网络小组所制定,专为远程登录会话和其他网络服务提供安全性的协议。
- OpenSSH 是 SSH 的开源实现,多数语境下 SSH 指的是 OpenSSH 里的 ssh 命令
- SSH 只是加密的 shell ,最初是用来替代 telnet 的。通过 port forward ,也可以让其他协议通过 SSH 的隧道而起到加密效果。
- SSH 和 TLS 是两种不一样的协议,但都能为其它协议提供加密效果
- 一般用 SSH 的,会称为 xxx over SSH , 会把 s 加在前面,例如 sftp (ftp over SSH)
- 一般用 TLS 的,会称为 xxx over TLS , 会把 s 加在后面,例如 ftps (ftp over TLS)
- SSH 能代理 UDP
- TLS 是建立在 TCP 上的,要代理 UDP 会有一点困难
DoH 和 DoT
- DNS over HTTPS 基于 HTTPS 的 DNS
- DNS over TSL 基于 TLS 的 DNS
- DoH 的安全原理与 DoT 一样,他们之间的区别只在于:DoH 有了 HTTP 格式封装,更加通用。
DNSSEC = 域名系统安全扩展(DNS Security Extensions),简称DNSSEC。通过数字签名来保证 DNS 应答报文的真实性和完整性,能够防止 DNS 欺骗和缓存污染等攻击。
TOTP = Time-based One-time Password (基于时间的一次性密码) 。RFC 6238
UUID = Universally Unique Identifier (通用唯一识别码)
DCE = Distributed Computing Environment (分布式计算环境)
口令 密码 密钥 令牌 的区别
- 密码学语境下
- 口令 password
- 通常是一个不太长的字符串,可以让人记住
- 如果没有特别声明,通常不是一次性的
- 密码 cipher
- cipher 指的是对消息进行加密或解密的算法
- 密钥 key
- 通常是一个很长的字符串,很难让人记住
- 如果没有特别声明,通常不是一次性的
- 令牌 token
- 通常是一个很长的字符串,很难让人记住
- 如果没有特别声明,通常是一次性的
- 通常还会包含一些用户信息
- 口令 password
- 一般语境下
- 主要混淆的是 口令 和 密码
- 口令 密码 都是 password
- cipher 可能会被翻译为 加密算法 之类的
- 密钥 和 令牌 的意思和密码学语境下的意思是一样的
- 密码学语境下
会议
- 安全
- ACM CCS
- ACM Conference on Computer and Communications Security
- IEEE S&P
- IEEE Symposium on Security and Privacy
- USENIX Security
- Usenix Security Symposium
- NDSS
- ISOC Network and Distributed System Security Symposium
- ACM CCS
- 密码学
- CRYPTO
- International Cryptology Conference
- 国际密码学会议
- 在美国开会,所以被称为 美密会
- EUROCRYPT
- International Conference on the Theory and Application of Cryptographic Techniques (EUROCRYPT)
- 密码学与信息安全理论与应用年度国际会议
- 在欧洲开会,所以被称为 欧密会
- ASIACRYPT
- International Conference on the Theory and Application of Cryptology and Information Security (ASIACRYPT)
- 密码学与信息安全理论与应用年度国际会议
- 在亚洲开会,所以被称为 亚密会
- 这三个都是由 IACR 主办的
- 国际密码学协会(IACR,International Association of Cryptological Research)
- CRYPTO
- 安全
信息安全
信息安全五要素
- 信息的保密性(Confidenciality)、真实性(Authenticity)、完整性(Integrity)、不可否认性(Non-repudiation)、可用性(Availability)
- 其中保密性(Confidenciality)、完整性(Integrity)、可用性(Availability)称为 CIA 三要素
- 这些要素是原子的,不能进一步分解或重合,代表信息安全的一个方面。任何信息安全漏洞都可以描述为影响这些要素一个或多个
- CIA 三要素之间存在互相牵制的关系,过度强化保密性时,将造成完整性与可用性的降低,需要高可用性的系统则会造成机密性与完整性的降低,因此在有限资源的前提下,在 CIA 三要素中获取适当的平衡是信息安全管理层次结构的重要课题。
各个密码学概念对应的要素
- 数字摘要 -> 完整性
- 消息认证码 -> 完整性 真实性
- 数字签名 -> 完整性 真实性 不可否认性
- 加密 -> 保密性
各种攻击所对应的要素
- 完整性 -> 篡改
- 真实性 -> 伪装
- 保密性 -> 窃听
- 可用性 -> 阻断
以 https 为例解释信息安全的五要素
https 的连接过程
- tcp 三次握手建立连接
- 客户端和服务器协商密码包
- 服务器发送证书给客户端,客户端验证证书,这里会验证证书的数字签名,有效时间,颁发者等信息,还会逐级验证证书链上的证书(证书链的验证会查询 OCSP 和 CRL )
- 客户端生成一个对称加密的密钥
- 客户端把对称加密的密钥发送给服务端;密钥交换协议有好多种,其中最容易理解的就是 RSA 了,客户端公钥加密 对称加密的密钥,然后服务器用私钥解密
- 客户端和服务器在后续的通讯都使用对称加密
数字证书和证书链是保证信息源的真实性
数字证书中的数字签名是保证数字证书的完整性,真实性,不可否认性
https 完成握手后的通讯使用对称加密是保证消息的保密性
对称加密的通讯会在消息的末尾加上 MAC 校验数据,保证消息的完整性,真实性
但这些都不能保证可用性,以下是针对可用性的攻击例子
- 拒绝服务攻击
- 旁观者攻击
信息安全常识
- 不要使用保密的密码算法
- 使用低强度的密码比不进行任何加密更危险
- 低强度的密码和没有加密同样不安全。但是使用了密码会给用户一种错误的安全感,导致用户容易泄露一些机密的信息。
- 任何密码总有一天都会被破解
- 密码只是信息安全的一部分
3A
- 认证(Authentication)
- 识别信息用户的身份,可记录信息被谁所访问使用,例如:透过密码或证书方式验证用户身份。
实务做法:
- 你所知道的(Something you know):账号/密码
- 你所拥有的(Something you have):IC卡、数字设备、数字签名、一次性密码(OTP)
- 你所具备的(Something you are):指纹、虹膜、声纹、脸部特征、静脉脉纹、DNA
- 识别信息用户的身份,可记录信息被谁所访问使用,例如:透过密码或证书方式验证用户身份。
实务做法:
- 授权(Authorization)
- 依照实际需求给予实体适当的权限,一般建议采最小权限(Least privilege),意即仅给予实际作业所需要的权限,避免过度授权可能造成的信息暴露或泄漏。
信息系统层面的实务访问控制方法分类如下:
- 强制访问控制(Mandatory Access Control)
- 自由选定访问控制(Discretionary Access Control)
- 以角色为基础的访问控制(Role-Based Access Control)
- 以规则为基础的访问控制(Rule-Based Access Control)
- 依照实际需求给予实体适当的权限,一般建议采最小权限(Least privilege),意即仅给予实际作业所需要的权限,避免过度授权可能造成的信息暴露或泄漏。
信息系统层面的实务访问控制方法分类如下:
- 记录(Accounting)
- 内容项目包含量测(Measuring)、监控(Monitoring)、报告(Reporting)与日志案(Logging), 以便提供未来作为审核(Auditing)、计费(Billing)、分析(Analysis)与管理之用, 主要精神在于收集用户与系统之间交互的资料,并留下轨迹纪录。
信息安全的基本原则
为了达到信息安全的目标,各种信息安全技术的使用必须遵守一些基本的原则。
- 最小化原则。
- 受保护的敏感信息只能在一定范围内被共享,履行工作职责和职能的安全主体,在法律和相关安全策略允许的前提下,为满足工作需要。仅被授予其访问信息的适当权限,称为最小化原则。敏感信息的“知情权”一定要加以限制,是在“满足工作需要”前提下的一种限制性开放。
- 可以将最小化原则细分为知所必须(need to know)和用所必须(need to use)的原则。
- 分权制衡原则。
- 在信息系统中,对所有权限应该进行适当地划分,使每个授权主体只能拥有其中的一部分权限,使他们之间相互制约、相互监督,共同保证信息系统的安全。
- 如果一个授权主体分配的权限过大,无人监督和制约,就隐含了“滥用权力”、“一言九鼎”的安全隐患。
- 安全隔离原则。
- 隔离和控制是实现信息安全的基本方法,而隔离是进行控制的基础。信息安全的一个基本策略就是将信息的主体与客体分离,按照一定的安全策略,在可控和安全的前提下实施主体对客体的访问。
- 在这些基本原则的基础上,人们在生产实践过程中还总结出的一些实施原则,他们是基本原则的具体体现和扩展。
- 包括:整体保护原则、谁主管谁负责原则、适度保护的等级化原则、分域保护原则、动态保护原则、多级保护原则、深度保护原则和信息流向原则等。
随机数
随机数的性质
- 随机性,不存在统计学偏差,是完全杂乱的数列
- 不可预测性,不能从过去的数列推测出下一个出现的数列
- 不可重现性,除非将数列本身保存下来,否则不能重现相同的数列
随机数的分类
- 随机数
- 伪随机数
- 统计学伪随机数(弱伪随机数) - 满足 随机性
- 密码学伪随机数(强伪随机数) - 满足 随机性 不可预测性
- 真随机数 - 满足 随机性 不可预测性 不可重现性
- 伪随机数
随机数的作用
- 生成密钥
- 用于对称密码和消息认证码
- 生成公钥密码
- 用于生成公钥密码和数字签名
- 生成初始化向量 IV
- 用于分组密码中的 CBC、CFB、OFB 模式
- 生成 nonce
- 用于防御重放攻击和分组密码中的 CTR 模式
- 生成盐
- 用于基于口令密码的 PBE 等
- 用随机数的目的是为了提高密文的不可预测性
在 linux 下生成随机数
/dev/random 在类 UNIX 系统中是一个特殊的设备文件,可以用作随机数生成器。
/dev/random 的随机数的提供是依赖与外部中断事件的,如果没有足够多中断事件,就会阻塞。 /dev/random 生成的是真随机数。
/dev/urandom(“unblocked”,非阻塞的随机数生成器)是 /dev/random 的一个副本 ,它会重复使用熵池中的数据以产生伪随机数据。 这表示对 /dev/urandom 的读取操作不会产生阻塞,但其输出的熵可能小于 /dev/random 的。 它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。
/dev/random 和 /dev/urandom 会输出二进制数据流,可以用 od 命令转换,或者用 base64 命令转换。
/dev/random 和 /dev/urandom 生成的都是符合密码学安全的随机数。因为 /dev/random 可能会阻塞,所以大部分情况下用 /dev/urandom 就可以了。
大部分情况下都是用 TRNG 生成的随机数作为种子,然后再用 CSPRNG 生成密码学安全的随机数。这样既能保证安全也能效率也不会太低。
命令行下的使用示例
# 不能直接用 cat ,因为 /dev/random 会一直输出
head -n 1 /dev/random | od -x
head -n 1 /dev/urandom | od -x
# 生成随机字符串
head -n 1 /dev/urandom | base64 | head -n 1
head -n 1 /dev/urandom | base64 | head -n 1
# 只生成数字
head -n 1 /dev/urandom | base64 | head -n 1 | tr -dc '0-9'
# 环境变量里的 $RANDOM 是一个随机数字
echo $RANDOM
在 windows 下生成随机数
windows api
- CryptGenRandom https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
- 重要 此 API 已弃用。 新软件和现有软件应开始使用 下一代加密 API。 Microsoft 可能会在将来的版本中删除此 API。
- CNG 是 windows 的下一代加密 API 。
bat 和 powershell
bat
echo %random%
powershell
// 生成一个随机数
Get-Random
// 生成一个小于等于 100 的随机数
Get-Random -Maximum 100
// 在这个范围内 [10.7, 20.93] 生成一个随机数
Get-Random -Minimum 10.7 -Maximum 20.93
// 在这个范围内 [1, 2, 3, 5, 8, 13] 随机选择 3 个数
Get-Random -InputObject 1, 2, 3, 5, 8, 13 -Count 3
- https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.utility/get-random
- https://learn.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.randomnumbergenerator
x86 汇编生成随机数
RDRAND 指令 和 RDSEED 指令
RDRAND 是英特尔 x86 cpu 中一条用于生成真随机数的指令。
英特尔在 Ivy Bridge 微架构中内置了一个利用电阻热噪声取得硬件真随机数的功能。 后续的 x86 cpu 都支持这个指令。 Ivy Bridge 就是第三代酷睿。 AMD 从哪一款 cpu 开始支持 RDRAND ,笔者没找到详细的资料,但是可以肯定的是 zen 架构及以后的 cpu 型号都支持。 基本可以肯定现在的 x86 cpu 都支持这条指令。 AMD 如何实现 RDRAND ,笔者也没有找到具体的资料,但从一些英文的文章来看, AMD 的 RDRAND 速度似乎比英特尔的要慢不少。
从英特尔的文档来看 RDRAND 和 RDSEED 的区别
- RDRAND 用于生成随机数, RDSEED 用于生成随机数种子
- RDRAND 和 RDSEED 是使用两种不同的硬件来生成的
- RDRAND 的速度更快, 但 RDSEED 的熵源质量更高
- 除非用于生成随机数种子,不然其它情况都应该用 RDRAND
RDRAND 和 RDSEED 都有可能调用失败。 英特尔的文档里建议应用程序在紧密循环中尝试 10 次重试,以防 RDRAND 指令不返回随机数。 这个数字基于二项式概率论证:考虑到 DRNG 的设计余量,连续十次失败的几率非常小,实际上表明 CPU 问题更大。
使用内联汇编调用 RDRAND 的例子 只能运行在 x86-64 的 cpu 上,只能运行在 64 位系统上,只能用 gcc 编译
/*
先判断 cpu 是否支持,
用 CPUID 指令来判断
再判断操作系统的位数,
用宏定义 __x86_64 来判断
再判断编译器
如果只是单纯地调用一次 RDRAND 指令,汇编语法用哪个都没关系的
gcc 或 clang 用 ATT 汇编
其它用 Intel 汇编
用宏定义 _MSVCRT_ 来判断
*/
#include <stdio.h>
#include <stdint.h>
#define DRNG_NO_SUPPORT 0x0
#define DRNG_HAS_RDRAND 0x1
#define DRNG_HAS_RDSEED 0x2
typedef struct cpuid_struct {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
} cpuid_t;
int get_drng_support();
void cpuid(cpuid_t *info, unsigned int leaf, unsigned int subleaf);
int rdrand16_step(uint16_t *rand);
int rdrand32_step(uint32_t *rand);
int rdrand64_step(uint64_t *rand);
int main()
{
if (!(get_drng_support() & DRNG_HAS_RDRAND)) {
printf("\ncurrent cpu is not support rdrand\n");
return 1;
}
uint64_t rand = 0;
unsigned int retries= 10;
unsigned int count= 0;
while (count <= retries) {
if (!rdrand64_step(&rand)) {
printf("\ncall rdrand fail\n");
return 1;
}
++count;
}
printf("\nRND64=0x%llx\n", rand);
return 0;
}
int get_drng_support()
{
static int drng_features= -1;
if ( drng_features == -1 ) {
drng_features= DRNG_NO_SUPPORT;
cpuid_t info;
cpuid(&info, 1, 0);
if ((info.ecx & 0x40000000) == 0x40000000) {
drng_features|= DRNG_HAS_RDRAND;
}
cpuid(&info, 7, 0);
if ((info.ebx & 0x40000) == 0x40000) {
drng_features|= DRNG_HAS_RDSEED;
}
}
return drng_features;
}
void cpuid(cpuid_t *info, unsigned int leaf, unsigned int subleaf)
{
asm volatile("cpuid"
: "=a" (info->eax), "=b" (info->ebx), "=c" (info->ecx), "=d" (info->edx)
: "a" (leaf), "c" (subleaf)
);
}
int rdrand16_step(uint16_t *rand)
{
unsigned char ok;
asm volatile ("rdrand %0; setc %1"
: "=r" (*rand), "=qm" (ok));
return (int) ok;
}
int rdrand32_step(uint32_t *rand)
{
unsigned char ok;
asm volatile ("rdrand %0; setc %1"
: "=r" (*rand), "=qm" (ok));
return (int) ok;
}
int rdrand64_step(uint64_t *rand)
{
unsigned char ok;
asm volatile ("rdrand %0; setc %1"
: "=r" (*rand), "=qm" (ok));
return (int) ok;
}
如果编译器不支持 RDRAND ,可以直接用16进制的指令调用,例如这样
#include <stdio.h>
int main()
{
long long unsigned int rnd64;
asm volatile(
".byte 0x48,0x0f,0xc7,0xf0\n"
"ret"
:"=r"(rnd64):
);
printf("\nRND64=0x%llx\n", rnd64);
return 0;
}
- https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html
- 英特尔 关于 DRNG 的文档
- 描述了随机数的概念
- CPU 中的 DRNG 基本原理
- RDRAND 和 RDSEED 的简单使用和示例代码
- https://crypto.stackexchange.com/questions/42340/usage-difference-between-x86-rdrand-and-rdseed
- https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
其它生成随机数的方式
- 让用户主动地提供随机的种子
- 像 PuTTY 在生成密钥那样
- 调用外部接口
- https://www.random.org
- 这个网站提供使用大气噪声生成的随机数
- 需要注册才能调用对应的 api
- 免费帐号的限额
- 每天最多 1000 个请求,每秒最多 10 个请求
- 严格许可用于开发和测试
可能是因为人们对 混沌系统 研究得不够深入才会觉得 大气噪声 或 电阻热噪声 是随机的
OpenSSL 的一般使用
OpenSSL是一个开放源代码的软件库包。这个包广泛被应用在互联网的网页服务器上。 其主要库是以C语言所写成,实现了基本的加密功能,实现了SSL与TLS协议。
以下命令均在 cygwin 或 linux 下运行
以下命令是在这个版本
OpenSSL 1.1.1g 21 Apr 2020
下的 OpenSSL 运行的
- 查看 openssl 的版本信息
openssl version -a
- 查看帮助,这里会输出 openssl 支持的算法
openssl help
- 查看某个命令的帮助
openssl 某个命令 --help
- 查看密码套件
openssl ciphers -v
- 数字摘要
echo "123" | openssl dgst -sha256
openssl dgst -sha256 文件路径
echo "123" | openssl dgst -sha256 | awk '{print $2}'
openssl dgst -sha256 文件路径 | awk '{print $2}'
- 输出当前时间戳
date +%s
- 输出纳秒 这是一个 9 位的数字 一些简单的伪随机数算法会使用纳秒作为种子
date +%N
- 生成 32 位随机字符串
openssl rand -base64 32
- 输出随机数字
# 输出随机数字,但无法确定数字的长度
openssl rand -base64 32 | tr -dc '0-9'
openssl rand -base64 64 | tr -dc '0-9'
# 生成16位随机数 有可能不足16位
openssl rand -base64 128 | tr -dc '0-9' | cut -c1-16
- 查看 对称加密命令和可以使用的算法
openssl enc -help
- enc 使用对称加密算法
openssl enc -aes-256-cfb -e -in a.txt -a -out b.txt -pass pass:123
openssl enc -aes-256-cfb -d -in b.txt -a -out c.txt -pass pass:123
# -aes-256-cfb 使用的算法
# -e 加密
# -d 解密
# -in 输入的文件路径
# -out 输出文件的路径
# -a 把输出转换成 base64 加密时有这个参数,解密时也要有这个参数
# -pass 数入密码和输入密码的方式
# pass
# file
# stdio
# env
# fd
- 生成 rsa 私钥
openssl genrsa -out rsa_private_key.pem 4096
# 默认是 pem 格式的
# -out 指定生成文件的路径
# 最后的 4096 是生成密钥的长度
# 生成的密钥对是 pkcs1 格式的, openssl 有相应的命令转成 pkcs8 或 pkcs12 格式
- 从私钥中提取公钥
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
# -pubout 提取公钥
# -out 指定生成文件的路径
# -in 私钥路径
- 公钥加密文件
openssl rsautl -encrypt -in a.txt -inkey rsa_public_key.pem -pubin -out b.txt
# -out 加密后的文件路径
# -in 需要加密的文件路径
# -inkey 公钥路径
- 私钥解密文件
openssl rsautl -decrypt -in b.txt -inkey rsa_private_key.pem -out c.txt
# -out 解密后的文件路径
# -in 需要解密的文件路径
# -inkey 私钥路径
- 使用私钥生成签名
openssl dgst -sha256 -sign rsa_private_key.pem -keyform PEM -out sign.sha256 a.txt
# -sha256 哈希算法
# -sign 私钥路径
# -keyform 私钥的格式
# -out 签名生成的路径
# 最后的 a.txt 是需要生成签名的文件路径
- 使用公钥验证签名
openssl dgst -sha256 -verify rsa_public_key.pem -keyform PEM -signature sign.sha256 a.txt
- 对文件的内容进行 base64 编码
openssl enc -base64 -e -in sign.sha256 -out sign.sha256.base64
- 对文件的内容进行 base64 解码
openssl enc -base64 -d -in sign.sha256.base64 -out sign.sha2562
- 比较两个文件的内容,如果两个文件内容一致则不会有输出
diff 文件1的路径 文件2的路径
- 生成一个 csr 文件
- 启动一个问题/回答的交互式会话,其它随便填就好,extra attributes 可以留空
- 其实 openssl 有一键生成密钥对, csr 和 证书的命令
openssl req -new \
-key rsa_private_key.pem \
-keyform PEM \
-out myserver.csr
# -new 生成一个新的 csr 文件
# -key 私钥文件路径
# -keyform 私钥的格式
# -out 生成的 csr 文件路径
- 查看 csr 文件内容
openssl req -text -in myserver.csr -noout -verify
# -in csr 文件路径
- 使用 csr 和 私钥生成自签证书
openssl x509 \
-sha256 \
-signkey rsa_private_key.pem \
-in myserver.csr \
-req -days 365 -out domain3.crt
# x509 生成 x509 格式的证书
# -sha256 证书采用的哈希算法
# -signkey 私钥路径
# -in csr 文件路径
# -days 证书有效天数
# -out 生成的证书路径
- 一条命令生成密钥和证书
openssl req -newkey rsa:4096 -nodes -keyout rsa_private_key.pem -x509 -days 365 -out domain.crt
openssl req -newkey rsa:4096 -nodes -keyout rsa_private_key.pem -x509 -days 365 -out domain.crt -subj "/C=CN/ST=State/L=City/O=Ltd/OU=Section/CN=localhost"
- 查看证书内容
openssl x509 -in domain.crt -noout -text
- 查看证书序列号
openssl x509 -in domain.crt -noout -serial
- 查看证书有效时间
openssl x509 -in domain.crt -noout -dates
生成多个域名的证书
一般使用 OpenSSL 生成证书时都是 v1 版的,不带扩展属性。 多域名证书需要用到 v3 版的 extensions 的 Subject Alternative Name (SAN 主题替代名称)
- 寻找默认配置文件
find / -name openssl.cnf
- 复制一份默认配置文件
cp /usr/ssl/openssl.cnf openssl.cnf
编辑 openssl.cnf
- [ req ] 字段下加入 req_extensions = v3_req
- [ v3_req ] 字段下加入 subjectAltName = @alt_names
- 在配置文件的最后最后新建一个字段 [ alt_names ]
- 在 [ alt_names ] 里按以下格式写入多个域名
[ alt_names ] DNS.1 = 3.example.com DNS.2 = 4.example.com
新建私钥
openssl genrsa -out rsa_private_key.pem 4096
- 生成 csr 文件
openssl req -new \
-key rsa_private_key.pem \
-keyform PEM \
-config openssl.cnf \
-out myserver.csr
- 生成数字证书
openssl x509 \
-sha256 \
-signkey rsa_private_key.pem \
-in myserver2.csr \
-extensions v3_req \
-extfile openssl.cnf \
-req -days 365 -out domain.crt
自建 CA
- 创建 CA 目录
mkdir -p ~/ssl/demoCA/{certs,newcerts,crl,private}
cd ~/ssl/demoCA
Touch index.txt
echo "01" > serial
- 寻找默认配置文件
find / -name openssl.cnf
- 复制一份默认配置文件
cp /usr/ssl/openssl.cnf ~/ssl/openssl.cnf
修改 openssl.cnf 文件
- 把 [ CA_default ] 的 dir 修改成 ~/ssl/demoCA/ 的绝对路径,类似于这样
[ CA_default ] dir = /root/ssl/demoCA # Where everything is kept
- 把 [ CA_default ] 的 dir 修改成 ~/ssl/demoCA/ 的绝对路径,类似于这样
生成 CA 根证书及密钥
openssl req -new -x509 -newkey rsa:4096 -nodes -keyout cakey.key -out cacert.crt -config openssl.cnf -days 365
- 生成客户端私钥
openssl genrsa -out client.key 4096
- 用该客户端私钥生成证书签名请求
openssl req -new -key client.key -out client.csr -config openssl.cnf
- 使用 CA 根证书签发客户端证书
openssl ca -in client.csr -out client.crt -cert cacert.crt -keyfile cakey.key -config openssl.cnf
- 注意:默认要求 国家,省,公司名称三项必须和CA一致
- 如果不想一致,可以修改 openssl.cnf 的 [ CA_default ] 的 policy 为 policy_anything
policy = policy_anything
证书链合并
一些情况下,从 CA 那里申请到的 SSL 证书需要配置证书链,因为颁发的 CA 只是一个中间 CA 。 这个时候,需要把 CA 的证书和 SSL 证书都转换成 pem 格式。 然后新建一个文件,按照 最终实体证书 -> 中间证书 -> 根证书 这样的顺序,把证书的 pem 格式的内容复制进去,证书之间用一个空行隔开。 例如这样
-----BEGIN CERTIFICATE-----
这是 最终实体证书
------END CERTIFICATE------
-----BEGIN CERTIFICATE-----
这是 中间证书
------END CERTIFICATE------
-----BEGIN CERTIFICATE-----
这是 根证书
------END CERTIFICATE------
根证书大多数情况下都会内置在客户端,所以大多数情况下都只需要 最终实体证书 和 中间证书。 有时 中间证书 可能有多个,按照签发顺序排列就好,反正就是下面的证书颁发上面的证书。
有时还需要把私钥和证书合并成一个文件,一般是把私钥放在前面,证书放在后面,例如 这样
cat server.key server.crt > server.pem
其它命令
openssl s_time 用于测试 TSL 服务
openssl s_time -connect www.baidu.com:443 -www /index.html
openssl s_server 用于测试 TSL 客户端,例如浏览器对各个加密套件的支持情况
openssl s_server -accept 2009 -key rsa_private_key.pem -cert domain.crt -www -debug -msg
# -accept 监听的端口 -key 私钥路径 -crt 证书路径 -www http请求返回状态信息
# -WWW 或 -HTTP 参数,则可以启动一个简单的静态服务器
# 如果不设置 -www -WWW -HTTP ,客户端在终端输入任何字符,服务端都会响应同样的字符给客户端
# 可以用浏览器输测试,直接输入网址 https://127.0.0.1:2009
# 可以用 curl 测试 curl -k -i -v https://127.0.0.1:2009
# 可以用 openssl s_client 测试 openssl s_client -connect 127.0.0.1:2009 -showcerts
openssl s_client 用于测试 TSL 服务端
- 可以像 telnet 那用测试端口
# 测试 example.com 的 80 端口是否有开启 openssl s_client -connect example.com:80
- 可以像 telnet 模拟 http 那样,模拟 https
# 输入这个命令后,会进入一个命令行交互界面,这时快速地输入 GET / HTTP/1.0 然后连续输入两个回车,就能返回网页内容 # -showcerts 在输出中显示服务器证书链的所有证书,而不仅仅是最后一个证书 openssl s_client -connect www.baidu.com:443 -showcerts
openssl smime 用于处理S/MIME邮件,它能加密、解密、签名和验证S/MIME消息
openssl ca ca命令是一个小型CA系统。它能签发证书请求和生成CRL。它维护一个已签发证书状态的文本数据库。
OpenSSH 的一般使用
OpenSSH (OpenBSD Secure Shell) 是 OpenBSD 的子项目。 OpenSSH 常常被误认以为与 OpenSSL 有关系,但实际上这两个项目有不同的目的,不同的发展团队,名称相近只是因为两者有同样的软件发展目标──提供开放源代码的加密通信软件。
程序主要包括了几个部分:
ssh | rsh rlogin rexec 与 Telnet 的替代方案 |
scp | rcp 的替代方案 |
sftp | ftp 的替代方案 |
sshd | SSH服务器 |
ssh-keygen | 产生 RSA 或 ECDSA 密钥,用来认证用 |
ssh-agent ssh-add | 帮助用户不需要每次都要输入密钥密码的工具 |
以下命令是在这个版本
OpenSSH_8.5p1, OpenSSL 1.1.1k 25 Mar 2021
下的 OpenSSH 运行的
sshd
sshd 的配置文件一般在这里 /etc/ssh/sshd_config
大部分情况下系统都会默认开启 sshd 服务,默认的端口号 22 ,默认监听 ip 0.0.0.0
- 如果要修改端口号,需要修改配置文件里的这个参数 Prot
- 如果要修改监听 ip ,需要修改配置文件里的这个参数 ListenAddress
- 配置修改后要重启 sshd
可用通过像这样的命令
ps -elf | grep sshd
判断 sshd 服务有没有开启可以直接执行 sshd 的可执行文件来启用 sshd 服务(需要是绝对路径)。 大部分情况下 sshd 的可执行文件是这个路径
/usr/bin/sshd
。如果运行
usr/bin/sshd
遇到这种错误sshd: no hostkeys available -- exiting
,那么可以先运行这句ssh-keygen -A
如果要关闭 sshd 可以先通过
ps -elf
找到对应的 pid ,然后 kill pid
ssh
ssh 的配置文件一般在这里
- 客户端配置 /etc/ssh/ssh_config
- 服务端配置 /etc/ssh/sshd_config
ssh 连接的配置一般在 ~/.ssh
- ~/.ssh/config 记录远程地址和默认用户名
- ~/.ssh/known_hosts 记录远程地址和远程地址的公钥指纹,如果服务器的公钥更换过(例如,重装系统),这里要删掉对应的记录,获取新的公钥指纹,不然连接会失败
ssh 用密码连接
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=30 -o TCPKeepAlive=yes 用户名@远程地址
# -o ServerAliveInterval 的意思是每 60 秒发送一次请求,用于保持连接的,不加这个参数也可以,但连接很容易就断开了
# -o TCPKeepAlive=yes 表示 TCP 保持连接不断开
# -o ServerAliveCountMax 表示连续 30 次服务端没有响应后,客户端就自动退出,如果有监控进程这类的,可以执行重新连接这类操作
- 如果需要用密钥连接,就在连接命令里加上 -i 参数
ssh -o ServerAliveInterval=60 -i 密钥路径 用户名@远程地址
ssh 的端口转发
ssh 除了登录服务器,还有一大用途,就是作为加密通信的中介, 充当两台服务器之间的通信加密跳板, 使得原本不加密的通信变成加密通信。 这个功能称为端口转发(port forwarding),又称 SSH 隧道(tunnel)。
ssh 的端口转发有三种
- 本地端口转发
- 一般语境下的 ssh 端口转发指的就是 本地端口转发
- 把本地某个端口的数据转发到代理的地址
- 其中一个应用场景就是把 ssh 服务器作为跳板机。甚至可以搞个二级跳板
- 通过 ssh 的端口转发连接内网 mysql 的例子
# 建立隧道 ssh -o ServerAliveInterval=60 -f -N -g -L 127.0.0.1:本地端口:MySQL地址:MySQL端口 用户名@远程地址 # -L 是建立转发 把本地的 9912端口转发到目标服务器地址的3306端口 # -N 不打开新的终端,连接只进行端口转发 # -f 后台运行 # -g 不清楚有什么用?允许远程主机连接到本地转发端口。如果在多路复用连接上使用,则必须在主进程上指定此选项。 # 通常 N f 会配合一起使用 # mysql 连接 mysql -h 127.0.0.1 -uMySQL用户名 --port=本地端口
- 远程端口转发
- 有时候远程端口转发也会被称为反向端口转发
- 会把 ssh 服务器上某个端口的数据转发到代理的地址
- 其中一个应用场景就是 xdebug 的远程调试,把远程 xdebug 的数据转发到本地的 IDE 中
- 通过 ssh 的远程端口转发 xdebug 的例子
ssh -o ServerAliveInterval=60 -f -N -g -R 127.0.0.1:远程端口:localhost:9003 用户名@远程地址
- 如果需要从外网访问,则需要修改一下 sshd 的设置,在 sshd 的配置文件里加上这一句,然后重启 sshd
GatewayPorts yes
- sshd 的配置文件通常在这个位置 /etc/ssh/sshd_config
- 重启 sshd 的命令 sudo systemctl restart sshd
- 动态端口转发
- 动态端口转发 实际上就是一个 socks 代理
- 动态端口转发 的例子
ssh -o ServerAliveInterval=60 -f -N -C -D 6080 用户名@远程地址 # -D 端口上的动态端口转发 # -C 压缩数据传输 # 本地其它程序可以通过类似这样的 url 连接 socks5://127.0.0.1:6080
- 还有 ssh -X 用于专门转发 x11 的数据
- 本地端口转发和远程端口转发最好显式声明地址,不显式声明地址可能存在安全隐患
ssh -L 127.0.0.1:本地端口:代理地址:代理端口 ssh -R 127.0.0.1:远程端口:代理地址:代理端口
ssh 文档 https://man.openbsd.org/ssh
https://wangdoc.com/ssh/port-forwarding
sftp
- sftp 连接服务器
sftp -o ServerAliveInterval=60 用户名@远程地址
sftp -o ServerAliveInterval=60 -i 密钥路径 用户名@远程地址
- sftp 命令参考
# 上传文件
put 本地路径 远程路径
# 下载文件
get 路径路径 本地路径
# 查看服务器路径[默认用户根目录]
pwd
# 列出当前服务器路径下的文件
ls -al
# 更改服务器路径,就是普通的 cd 命令
cd
# 在服务器里新建文件夹
mkdir 文件夹名
# 删除服务器的文件
rm 文件名
# 删除服务器的文件夹
rm -r 文件夹名
# 查看本地路径
lpwd
# 列出本地路径下的文件
lls
# 更改本地路径,和 cd 一样只是前面多了个 l
lcd
# 在本地新建文件夹
lmkdir 文件夹名
# 执行其它本地命令,这里要注意本地的命令行环境
!本地命令
OpenSSH 和 OpenSSL
OpenSSH 是 SSH 协议的实现,实现过程中,需要用到密钥交换算法,对称/非对称加密算法,随机数算法,等密码学相关的算法。 早期版本的 OpenSSH 是通过调用 OpenSSL 的库来实现这些算法的。 2014年4月的心脏出血漏洞事件之后, OpenBSD 项目成员以 OpenSSL 1.0.1g 作为分支,创建一个名为 LibreSSL 的项目。 OpenSSH 会逐渐减少对 OpenSSL 的依赖。
GnuPG 的一般使用
1991 年,程序员 Phil Zimmermann 为了避开政府的监视,开发了加密软件 PGP (Pretty Good Privacy)。 但是,它是商业软不能自由使用。所以,自由软件基金会决定,开发一个 PGP 的替代品取名为 GnuPG (GNU Privacy Guard)。 OpenPGP (Pretty Good Privacy) 是一种加密标准。 GnuPG 是实现该标准的软件。
gpg 在需要用到私钥的地方都需要口令。 gpg 会对消息进行压缩。
gpg 的配置文件一般会保存在这个位置
linux ~/.gnupg
windows 系统盘/Users/用户名/.gnupg
加密和解密
加密流程
- 压缩消息
- 生成随机数作为会话密钥
- 使用会话密钥加密消息
- 使用接收者ID加密会话密钥
- 把加密后的消息和加密后的会话密钥合并在一起
- 有时还会有这一步,把合并后的数据转换为文本
解密流程 基本上就是加密流程的逆向
签名和验签
签名
- 用口令解密私钥
- 生成消息的数字摘要
- 用私钥加密消息的数字摘要
验签
- 用公钥解密消息的数字签名
- 生成消息的数字摘要
- 对比解密后的数字签名和消息的数字摘要是否一致
一些命令参考
以下命令是在这个版本
gpg (GnuPG) 2.2.28
下的 GnuPG 运行的
- 查看帮助
gpg --help
- 新建密钥
gpg --gen-key
# 这里会要求输入用户名和邮箱,用户名和邮箱组合起来就是要用户ID了
# 例如 用户名是 username ,邮箱是 email@123.com ,那么用户ID 就是 username <email@123.com> ,还有就是在命令行里输入时要记得加上双引号
- 列出系统中已有的密钥
gpg --list-keys
- 导出公钥
gpg --armor --output public-key.txt --export [用户ID]
- 导出私钥 需要输入密码
gpg --armor --output private-key.txt --export-secret-keys [用户ID]
- 导入密钥,可以是公钥也可是私钥
gpg --import [密钥文件]
- 加密
gpg --armor --recipient [用户ID] --output demo.en.txt --encrypt demo.txt
- 解密
gpg --recipient [用户ID] --output demo.de.txt --decrypt demo.en.txt
- 签名
gpg --armor --local-user [用户ID] --output demo.txt.sig --detach-sign demo.txt
- 验证签名
gpg --armor --recipient [用户ID] --output demo.txt.sig --verify demo.txt
- 签名 + 加密
gpg --armor --local-user [发信者ID] --recipient [接收者ID] -output demo.txt.asc --sign --encrypt demo.txt
# 直接解密即可,解密的同时会验证签名
gpg --recipient [接收者ID] --output demo.de.txt --decrypt demo.txt.asc
- 常用的命令参数解释
- --armor 把数据转换成 ASCII 码
- --recipient 用于解密的用户ID 如果只在本地使用,大部分情况下都使用这个参数输入用户ID的
- --local-user 用于签名和加密的用户ID
- --output 输出的文件路径
GnuPG 和 email
很多邮件客户端都支持使用 GPG 来加密邮件。
只要导入发件人的私钥和收件人的公钥,就能发送加密的邮件。发件人的邮箱地址需要和私钥的邮箱地址对应,收件人的邮箱地址需要和公钥的邮箱地址对应。
使用加密后,会把整个邮件报文加密,包括主题,然后把密文保存在一个文件里,作为附件发送。
如果收件人的邮件客户端不支持直接解密邮件,可以把邮件的附件下载下来然后再用 GPG 解密。像这样 gpg --recipient [接收者ID] --output demo.de.txt --decrypt encrypted.asc
其实把明文加密后再把密文复制到邮件内容里发送也可以,但收件人的邮件客户端未必能解密,因为一般加密的邮件都是整个报文加密的,这种可能需要把邮件里的密文保存到单独的文件后再解密。
参考
https://zh.wikipedia.org/wiki/%E5%85%AC%E9%92%A5%E5%AF%86%E7%A0%81%E5%AD%A6%E6%A0%87%E5%87%86
https://zh.wikipedia.org/wiki/%E4%BF%A1%E6%81%AF%E5%AE%89%E5%85%A8
https://yeasy.gitbook.io/blockchain_guide/ 区块链技术指南 虽然是讲区块链的,但密码学部分很有参考价值
https://book.douban.com/subject/26822106/ 图解密码技术(第3版)
https://www.gnupg.org/howtos/zh/index.html GnuPG 袖珍 HOWTO (中文版)
https://www.crypto101.io/ 密码学入门课程