国密算法
Author: @landend
Time:04C8B2E950255AB66850FBEED9170D5ACF736B88DAC96520919E9186E143F5715E4B34A3C132FF832AA79AF97CDBC27A63C733E6D2C96E6D7576B4F5C9BD38C468235FBB3664A006CF40524D1BFD50DAE86BCB899497CF1638D7F6F572B5938F60720331A296A15EF0200B7F82B168007E979F(4CC6F19F15E22214E3EF4A559AD239BC473A1D20A5B294799AAB844731ABEE31/0460CBAAE00082B0D882C9511B53B9C1662D01A654F13DD13E04A444770F3D8FB712F6012502478C48C5276758A5359B22D36FD3354513C0D2926E228845F5FA0D)
一、国密算法概述
1、国密算法介绍
国家密码管理局全称为国家商用密码管理办公室,与中央密码工作领导小组办公室,隶属于中共中央直属机关的下属机构。
国家密码局认定的国产密码算法被称为国密,即商用密码。
商用密码技术,是指能够实现使用密码算法的加密、解密和认证等功能的技术,包括了密码算法编程技术和密码算法芯片、加密卡等的实现技术。
商用密码技术是商用密码的核心,国家将商用密码技术列入国家秘密,任何单位和个人都有责任和义务保护商用密码技术的秘密。
2、国密算法的意义
信息安全已上升到国家安全的战略地位,保证安全最根本的方法是基础软件和基础硬件都是自己控制。
在无法真正实现软件和硬件全部国产化的情况下,数据安全作为信息安全的重要组成部分,如何保障安全传输就成为了一个重要的议题,而加密算法正是数据传输安全与否的核心。
随着金融安全上升到国家安全高度,近年来国家有关机关和监管机构站在国家安全和长远战略的高度提出了推动国密算法应用实施、加强行业安全可控的要求。摆脱对国外技术和产品的过度依赖,建设行业网络安全环境,增强我国行业信息系统的“安全可控”能力显得尤为必要和迫切。
美国、英国、德国、法国、俄罗斯等基本都强制禁止一切所谓的国际标准算法介入自身核心国产产品。
国际标准加密算法存在不可预知的问题,除了众所周知的设计漏洞,如 MD5 和 SHA-1 本身存在碰撞攻击,RSA 公钥公因数问题等,算法设计后门才是真正让国家担心的。
往算法中插入后门,和检测并证明后门的存在之间,在数学上是非常不对称的。数学后门的查找非常非常困难。比如被使用了接近半个世纪的 RSA 就被斯诺登爆料 NSA 花千万美元让 RSA Security 在其加密工具集中,默认使用脆弱的双椭圆曲线随机数生成算法(2017年该算法被从标准中取消)。
关于很多加密标准的算法专利大多数都掌握在一些特定公司的手上,拥有自己的算法专利以免被掣肘也是一个非常重要的战术考量。
二、国密算法标准
1、国密算法类别
为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,包括 SM1(SCB2) 、SM2 ( 商密二号算法 ) 、SM3 、SM4 、SM7 、SM9 、祖冲之密码算法( ZUC )等。
SM1 、SM4 、SM7 、祖冲之密码( ZUC )是对称算法;SM2 、SM9 是非对称算法;SM3 是哈希算法。其中 SM1、SM7 算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
2、SM1 算法
SM1 为对称加密,分组长度和密钥长度都是 128 比特,其加密强度与 AES 相当。该算法不公开,仅以 IP 核的形式存在于芯片中。调用该算法时,需要通过加密芯片的接口进行调用。
采用该算法已经研制了系列芯片、智能 IC 卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。
3、SM2 算法
SM2 算法是我国在 ECC 椭圆曲线算法基础上自主设计的公钥密码实现,包括 SM2-1 椭圆曲线数字签名算法,SM2-2 椭圆曲线密钥交换协议,SM2-3 椭圆曲线公钥加密算法,分别用于实现数字签名密钥协商和数据加密等功能。不同于 ECDSA(数字签名)、ECDH(密钥交换) 等国际标准,SM2 算法采取了更为安全的机制,它避免可能存在的弱曲线,对抗已知的攻击手段,推荐了一条 256 位的曲线作为标准曲线。
国内目前主要采用 RSA 算法, 正在陆续过渡到 SM2 国密算法。在相同安全性能下,RSA 算法所需的公钥数要多于 SM2 算法,160位的SM2算法与1024位的RSA算法具有相同的安全等级。同等安全强度下,ECC 密钥的长度比 RSA 密钥的长度要小得多,并且,随着 RSA 密钥的翻倍,ECC 密钥并不需要翻倍就能获取同等的安全强度。这一点对于移动设备、嵌入式以及其它功耗受限、计算能力受限的场景来说就很有用了。
RSA key size (bits) | ECC key size (bits) |
---|---|
1024 | 160 |
2048 | 224 |
3072 | 256 |
7680 | 384 |
15360 | 521 |
SM2 安全强度比 RSA 2048 位高,并且运算速度快于 RSA 。
算法 | RSA | SM2 |
---|---|---|
算法结构 | 基于大因数分解 | 基本椭圆曲线 |
计算复杂度 | 亚指数级 | 完全指数级 |
存储空间 | 2048-4096bit | 192-256bit |
密钥生成速度 | 慢 | 较RSA算法快百倍以上 |
解密加密速度 | 一般 | 较快 |
- SM2 加解密
package main
import (
"bytes"
"crypto/rand"
"fmt"
"os"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
)
// 生成密钥对函数
func GerenateSM2Key() {
// 生成 sm2 密钥对
privateKey, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
// 通过 x509 将私钥反序列化并进行 pem 编码( base64 格式)
privateKeyToPem, err := x509.WritePrivateKeyToPem(privateKey, nil)
if err != nil {
panic(err)
}
// 将私钥写入磁盘文件
file, err := os.Create("sm2Private.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(privateKeyToPem)
if err != nil {
panic(err)
}
// SM2 公钥实例化
publicKey := privateKey.Public().(*sm2.PublicKey)
// 将公钥通过x509序列化并进行pem编码
publicKeyToPem, err := x509.WritePublicKeyToPem(publicKey)
if err != nil {
panic(err)
}
// 将公钥写入磁盘文件
file, err = os.Create("sm2Public.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(publicKeyToPem)
if err != nil {
panic(err)
}
}
// 加密函数
func EncryptSM2(plainText []byte, pubFileName string) []byte {
// 打开公钥文件读取公钥
file, err := os.Open(pubFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
// 将 pem 格式的公钥解码并反序列化
publicKeyFromPem, err := x509.ReadPublicKeyFromPem(buf)
if err != nil {
panic(err)
}
// 加密实现
cipherText, err := publicKeyFromPem.EncryptAsn1(plainText, rand.Reader)
if err != nil {
panic(err)
}
return cipherText
}
// 解密函数
func DecryptSM2(cipherText []byte, priFileName string) []byte {
// 打开并读取私钥
file, err := os.Open(priFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
// 将 pem 格式私钥文件解码并反序列化
privateKeyFromPem, err := x509.ReadPrivateKeyFromPem(buf, nil)
if err != nil {
panic(err)
}
// 解密实现
planiText, err := privateKeyFromPem.DecryptAsn1(cipherText)
if err != nil {
panic(err)
}
return planiText
}
func main() {
// 根据函数生成密钥对
GerenateSM2Key()
// 加密的数据
src := []byte("使用SM2加密的数据:任务网络")
// 根据函数实现加密
cipherText := EncryptSM2(src, "sm2Public.pem")
sText := string(cipherText)
fmt.Println("加密结果==>", sText)
// 解密数据
plainText := DecryptSM2(cipherText, "sm2Private.pem")
flag := bytes.Equal(plainText, src)
fmt.Println("解密是否成功:", flag)
text := string(plainText)
fmt.Println("解密后数据==>", text)
}
- SM2 数字签名
package main
import (
"crypto/rand"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
"os"
)
// 创建密钥对函数
func GerenateSM2Key(){
// 生成 sm2 密钥对
privateKey, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
// 通过 x509 将私钥反序列化并进行 pem 编码
privateKeyToPem, err := x509.WritePrivateKeyToPem(privateKey, nil)
if err != nil {
panic(err)
}
// 将私钥写入磁盘文件
file, err := os.Create("sm2Private.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(privateKeyToPem)
if err != nil {
panic(err)
}
// SM2 公钥实例化
publicKey := privateKey.Public().(*sm2.PublicKey)
// 将公钥通过 x509 序列化并进行 pem 编码
publicKeyToPem, err := x509.WritePublicKeyToPem(publicKey)
if err != nil {
panic(err)
}
// 将公钥写入磁盘文件
file, err = os.Create("sm2Public.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(publicKeyToPem)
if err != nil {
panic(err)
}
}
// 签名函数实现
func SignSM2(plainText []byte, priFileName string) []byte {
// 打开并读取私钥
file, err := os.Open(priFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
// 将 pem 格式私钥文件解码并反序列化
privateKeyFromPem, err := x509.ReadPrivateKeyFromPem(buf, nil)
if err != nil {
panic(err)
}
// 签名实现
sign, err := privateKeyFromPem.Sign(rand.Reader, plainText, crypto.SHA256)
if err != nil {
panic(err)
}
return sign
}
// 签名校验函数
func VerifySM2(plainText, signed []byte, pubFileName string) bool {
// 打开公钥文件并读取公钥
file, err := os.Open(pubFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
// 将 pem 格式公钥解码并反序列化
publicKeyFromPem, err := x509.ReadPublicKeyFromPem(buf)
if err != nil {
panic(err)
}
// 签名校验
verify := publicKeyFromPem.Verify(plainText, signed)
return verify
}
func main(){
src := []byte("使用SM2椭圆曲线算法进行的签名验签测试数据")
signSM2 := SignSM2(src, "sm2Private.pem")
flag := VerifySM2(src, signSM2, "sm2Public.pem")
fmt.Println("签名校验结果:", flag)
}
4、SM3 算法
SM3 是我国自主设计的哈希算法,是一种密码散列函数标准,由国家密码管理局与 2010年12月17日 发布,适用于商用密码应用中的 消息认证码 的生成与验证和 数字签名 以及 伪随机数 的生成,可满足多种密码应用的安全需求。
SM3 算法公开,为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短。SM3 算法对输入长度小于 2 的 64 次方的比特消息,经过填充和迭代压缩,生成长度为 256 比特的杂凑值,其中使用了异或、模、模加、移位、与、或、非运算,由填充、迭代过程、消息扩展和压缩函数所构成。因此 SM3 算法的安全性要高于MD5 算法和 SHA-1 算法,其效率与 SHA-256 相当。
SM3算法采用Merkle-Damgard结构,消息分组长度为512位,摘要值长度为256位。
SM3算法的压缩函数与SHA-256的压缩函数具有相似的结构,但是SM3算法的设计更加复杂,比如压缩函数的每一轮都使用2个消息。
目前主要采用 MD5/SHA 算法,正在陆续过渡到 SM3 国密算法。SM3 算法与国际 SHA 算法相对应, 特别与 SHA-2 算法类似
算法 | MD5 | SHA-256 | SM3 |
---|---|---|---|
输入位数 | ∞ | 264 | 264 |
输出位数 | 128 | 256 | 256 |
迭代次数 | 64 | 64 | 64 |
分组长度 | 512 | 512 | 512 |
强度 | 弱 | 中 | 中 |
- SM3 散列
package main
import (
"fmt"
"github.com/tjfoc/gmsm/sm3"
)
func main() {
// 散列的原始数据
src := []byte("这是任务网络使用SM3算法实现的单向散列函数")
// SM3 散列的实现
hash := sm3.New()
hash.Write(src)
hashed := hash.Sum(nil)
fmt.Printf("散列结果为:%x", hashed)
}
运行结果:
散列结果为:de00aabe616799508120f604e22830e0a5d48f5dc353d823af725547932e08f8
5、SM4 算法
2021年6月25日,我国 SM4 分组密码算法正式成为 ISO/IEC 国际标准,标志着我国商用密码算法国际标准体系进一步完善,展现了我国先进的密码科技水平和国际标准化能力,对提升我国商用密码产业发展,推动商用密码更好服务“一带一路”建设具有重要意义。
SM4 算法是在国内广泛使用的 WAPI 无线网络标准中使用的加密算法,是一种32轮的迭代非平衡 Feistel 结构的分组加密算法,其密钥长度和分组长度均为128一个分组算法,用于无线局域网产品。
SM4 算法的加解密过程中使用的算法是完全相同的,唯一不同点在于该算法的解密密钥是由它的加密密钥进行逆序变换后得到的。
SM4 算法采用非线性迭代结构,每次迭代由一个轮函数给出,其中轮函数由一个非线性变换和线性变换复合而成,非线性变换由S盒所给出。其中 rki 为轮密钥,合成置换 T 组成轮函数。轮密钥的产生与上图流程类似,由加密密钥作为输入生成,轮函数中的线性变换不同,还有些参数的区别。
国产SM4算法在计算过程中增加非线性变换,大大提高了其算法的安全性,和国际标准的对比参考
算法 | 3DES | AES-256 | SM4 |
---|---|---|---|
算法结构 | 使用标准的算术和逻辑运算 | 基于 SPN 结构 | 非平衡Feistels |
计算轮数 | 48 | 14 | 32 |
分组长度 | 128位 | 128位 | 128位 |
密钥长度 | 128位 | 256位 | 128位 |
有效密钥长度 | 128位 | 128位 | 112位 |
性能 | 中 | 快 | 快 |
安全性 | 中 | 高 | 高 |
-
SM4 加解密
SM4 支持分组密码模式中的 CBC 和 ECB 两种模式,CBC 需要设定初始值。这里使用 CBC 模式,数据填充标准为 PKCS7
package main
import (
"bytes"
"fmt"
"github.com/tjfoc/gmsm/sm4"
)
func main() {
// 原始明文数据
src := []byte("任务网络使用国密SM4算法的CBC模式进行测试")
// 对称密钥,根据 SM4 要求不能超过128bit,密钥最多也就是16位
key := []byte("q1w2e3r4t5y6u7i8")
// 加解密实现
cipherText, err := sm4.Sm4Cbc(key, src, true)
if err != nil {
panic(err)
}
fmt.Println("加密结果", string(cipherText))
plainText, err := sm4.Sm4Cbc(key, cipherText, false)
if err != nil {
panic(err)
}
fmt.Println("解密结果:", string(plainText))
flag := bytes.Equal(src, plainText)
fmt.Println("SM4加解密实现,数据填充标准为PKSC7,是否解密成功:", flag)
}
6、SM7 算法
SM7算法,是一种分组密码算法,该算法标准目前已经公开。
SM7 算法分组长度为128比特,密钥长度为128比特。SM7适用于非接触式IC卡,应用包括身份识别类应用(门禁卡、工作证、参赛证),票务类应用(大型赛事门票、展会门票),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通等)。
7、SM9 算法
为了降低公开密钥系统中密钥和证书管理的复杂性,以色列科学家、RSA 算法发明人之一 Adi Shamir 在1984年提出了标识密码( Identity-Based Cryptography )的理念。标识密码将用户的标识(如邮件地址、手机号码、QQ 号码等)作为公钥,省略了交换数字证书和公钥过程,使得安全系统变得易于部署和管理,非常适合端对端离线安全通讯、云端数据加密、基于属性加密、基于策略加密的各种场合。2008 年标识密码算法正式获得国家密码管理局颁发的商密算法型号:SM9 ,为我国标识密码技术的应用奠定了坚实的基础。
SM9算法对标国际IBC算法,是基于标识的非对称密码算法体系,该算法于2016年正式发布,在应用的成熟度方面还在不断完善,目前在电子邮件领域有一些实际应用。
SM9算法不需要申请数字证书,适用于互联网应用的各种新兴应用的安全保障。如基于云技术的密码服务、电子邮件安全、智能终端保护、物联网安全、云存储安全等等。这些安全应用可采用手机号码或邮件地址作为公钥,实现数据加密、身份认证、通话加密、通道加密等安全应用,并具有使用方便,易于部署的特点,从而开启了普及密码算法的大门。
8、ZUC祖冲之算法
祖冲之算法是中国自主研究的流密码算法,是我国第一个走向国际的国密算法,对标国际的 RC4 算法,属于对称密码算法中的序列密码算法。目前主要是承担 LTE 网络中语音流加密的功能,是运用于移动通信4G网络中的国际标准密码算法。该算法包括祖冲之算法( ZUC )、加密算法( 128-EEA3 )和完整性算法( 128-EIA3 )三个部分。目前已有对 ZUC 算法的优化实现,有专门针对 128-EEA3 和 128-EIA3 的硬件实现与优化。
三、附录
1、ECC椭圆曲线加密算法
椭圆曲线密码学,是一种建立公开密钥加密的算法,也就是非对称加密。ECC被公认为在给定密钥长度下最安全的加密算法。
ECC英文全称 “ Ellipese Curve Cryptography “ 与传统的基于大质数因子分解困难性的加密方法不同,ECC通过椭圆曲线方程式的性质产生密钥,具体就是将椭圆曲线上的特定点进行特殊的乘法运算来实现。
ECC 164 位的密钥产生的安全级别相当于 RSA 1024 位密钥提供的保密强度,而且计算量级较小,处理速度更快,存储空间和传输带宽占用较小。我们国家居民二代身份证正在使用256位的椭圆曲线密码,包括比特币、以太坊也选择ECC作为加密算法。
-
ECC算法原理:
在大多数 A 和 B 的选值下,连接两点都应该一定和椭圆曲线相交于第三点。过这个第三点,作与 Y 轴平行的直线,穿过 X 轴,与椭圆曲线再次相交,如上图中这个交点 C 就是 A 和 B 两个点的加和。
假设椭圆曲线上出现的 A 点和 B 点都在同一个位置,这时就作 A 点关于椭圆曲线的切线。遵循和上面同样的原则,找到 C 点。
ECC 的安全性在于以目前的计算机能力无法作逆运算,这个逆过程也就是分解离散对数问题。我们无法通过 C 获取到 A 点和 B 点,特别是当使用一个非常大的数值,穷举应该需要若干个宇宙时间(从有到无或者从无到有)。
2、证书标准规范 X.509
X509 协议,是 PKI 技术体系中应用最为广泛、也是最为基础的一个国际标准。它的主要目的在于定义一个规范的数字证书的格式。大多数认证机构颁发的证书,都使用由 ITU (国际电信联盟)和 ISO (国际标准化组织)制定的 X.509 规范。
X.509 证书包括:
- 签名前的证书--签名对象的信息
- 数字签名算法--对证书签名时所使用的算法
- 数字签名--对证书使用数字签名
X.509 证书编码格式:
- .DER :用二进制 DER 编码的证书,都是二进制格式
- .PEM :用 ASCLL 编码的证书,都是 base64 格式
X.509 文件扩展:
- .crt :证书文件,编码方式有可能是 .pem ,也有可能是 .der
- .cer :证书文件,多用于 Windows 平台,编码方式有可能是 .pem,也有可能是 .der
- .csr :证书签名请求,是向权威证书颁发机构获得签名证书的申请,其实就是一个带有自身机构信息的公钥。通常在生成这个申请的时候,同时也会生成一个私钥
3、消息认证码
消息认证码( message authentication code )是一种确认完整性并进行认证的技术。消息认证码的输入包括任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据就是 MAC 值。
使用单向散列函数( SM3 )可以构造消息认证码。消息认证码是将“发送者和接收者之间的共享密钥”和“消息”进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。
不过消息认证码无法防止否认和第三方证明,比如如何证明这个数据是发送者发送的,这就需要数字签名弥补这些问题。
4、数字签名
数字签名是现实世界中的签名( sign )和盖章这样的行为在数字世界中的重现。
数据签名有两种实现方式:直接对消息签名和对消息的散列值进行签名。
数字签名需要结合非对称加密等技术,处理过程非常耗时,因此一般不会直接对整个消息内容直接添加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值进行数字签名。
数字签名是利用了“没有私钥的人事实上无法生成使用该私钥的密文”来实现的,和公钥密码有着紧密的联系,简单理解就是通过私钥加密来生成签名,而用公钥解密来验证签名。
5、伪随机数
使用单向散列函数可以构造伪随机数生成器。密码技术中使用的随机数需要具备“事实上不可能根据过去的随机数列预测未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。
随机数的性质:
- 随机性--不存在统计学偏差,是完全杂乱的数列,这类代表弱伪随机数
- 不可预测性--不能从过去的数列推测出下一个出现的数,这类代表强伪随机数
- 不可重现性--除非将数列本身保存下来,否则不能出现相同的数列,这类代表真随机数
随机数使用场景:
- 生成对称密码和消息认证码
- 用于公钥密码和数字签名
- 用于分组密码的 CBC 、CFB 和 OFB 模式
- 用于防御重放攻击以及分组密码的 CTR 模式等
- 用于口令的 salt 生成
6、WAPI
WAPI ( Wireless LAN Authentication and Privacy Infrastructure )是我国自主研发的,拥有自主知识产权的无线局域网强制(所以苹果、三星等国内销售手机都必须支持)标准中的安全机制。当前全球无线局域网领域仅有的两个标准,分别是美国行业标准组织提出的 IEEE 802.11 系列标准,以及中国提出的 WAPI 标准。
7、Feistel 网络
DES 的基本结构是由 Horst Feistel 设计的,因此被称为 Feistel 网络或者 Feistel 结构。
在 Feistel 结构中,加密的各个步骤被称为轮 ( round ) ,整个加密过程就是进行若干次轮的循环。每一轮都需要使用一个不同的子密钥。
Feistel 网络的轮数可以任意增加,加密时无论使用任何函数作为轮函数都能实现正确解密,这样加密和解密都可以用完全相同的结构来实现,保证了加密的结果必定能正确解密。但 AES 最终选择的 Rijndael 算法并没有使用 Feistel 网络,而是 SPN 结构。
8、SPN结构
简称SP结构,其中S( Substitution )表示替代,也叫 s 盒;P( Permutation )表示置换。
-
S 盒能抵御统计攻击,这是密码攻击的基本安全保证,主要使用了混淆和扩散两种技术。
- 混淆就是改变数据块,使输出位与输入位之间不能通过明显的统计关系逆向。因为输入数据和输出数据之间未必长度一样,也未必需要一一对应。比如输入 3 组数据,输出可以是 2 组或者 5 组,当然也可以是等进等出。
- 扩散就是通过密钥位转移到密文的其他位置上,也就是移位。
-
P 置换用来交换各个数据位的位置
9、分组密码模式
分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的模式。
-
ECB
电码本模式( Electronic Codebook Book ),这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。ECB 是所有模式中最简单的一种,明文分组和密文分组一一对应。这种构造下很容易对密文进行判断,并作为线索破译密码,另外 ECB 模式可以在不破译密文的情况下操纵明文,所以实际生产中不建议使用该模式。
-
CBC
密码分组链接模式( Cipher Block Chaining ),这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密,最终密文分组就像链条一样相互连接在一起。ECB 模式中的缺陷在 CBC 模式下都不存在,所以 IPsec 就使用该模式来确保互联网通信机密。在工作或者生产环境中,我们也推荐使用该模式。
-
CFB
密文反馈模式( Cipher FeedBack ),这种模式下前一个密文分组会被送回到密码算法的输入端,反馈,指的就是返回输入端的意思。在 ECB 和 CBC 模式中,明文分组都是通过密码算法进行加密,然而在 CFB 模式中是将块密码变为自同步的密钥流(密码算法生成的比特序列称为密钥流)。CFB 模式解密时仍然使用分组密码算法执行加密操作,只是反向而已。实际工作中推荐使用 CTR 模式代替该模式。
-
OFB
输出反馈模式( Output feedback ),OFB 模式和 CFB 模式的区别仅仅在于密码算法的输入。CFB 模式中密码算法的输入是前一个密文分组,OFB 模式中,密码算法的输入是密码算法的前一个输出。这种模式是先用块加密器生成密钥流( Keystream ),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。实际工作中推荐使用 CTR 模式代替该模式。
-
CTR
计数器模式( CounTeR),这是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。在 CTR 模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成密钥流。最终的密文分组是通过将计数器加密得到的比特序列,与明文分组进行异或得到的,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。推荐使用该模式。
10、PKCS7
PKCS是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关标准。
PKCS7就是PKCS众多标准中的一个,它定义一种通用的消息语法,包括数字签名和加密等用于增强的加密机制,PKCS7与PEM兼容,所以不需其他密码操作,就可以将加密的消息转换成PEM消息。