国密算法了解与实践

了解

国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

  • SM1 为对称加密。

    其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
    采用该算法已经研制了系列芯片、智能IC卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。

  • SM2为非对称加密,基于ECC。

    该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。

  • SM3 消息摘要。

    可以用MD5作为对比理解。该算法已公开。校验结果为256位。

  • SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。

实践

基于同济研究院国密算法。其实现了 GM SM2/3/4 library based on Go。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SM2: 国密椭圆曲线算法库
. 支持Generate Key, Sign, Verify基础操作
. 支持加密和不加密的pem文件格式(加密方法参见RFC5958, 具体实现参加代码)
. 支持证书的生成,证书的读写(接口兼容rsa和ecdsa的证书)
. 支持证书链的操作(接口兼容rsa和ecdsa)
. 支持crypto.Signer接口

SM3: 国密hash算法库
. 支持基础的sm3Sum操作
. 支持hash.Hash接口

SM4: 国密分组密码算法库
. 支持Generate Key, Encrypt, Decrypt基础操作
. 提供Cipher.Block接口
. 支持加密和不加密的pem文件格式(加密方法为pem block加密, 具体函数为x509.EncryptPEMBlock)
x509: 证书
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import (
"bytes"
"crypto/rand"
"fmt"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/sm3"
"log"
"testing"
)

func TestSM3(t *testing.T) {
data := "test"
h := sm3.New()
h.Write([]byte(data))
sum := h.Sum(nil)
fmt.Printf("digest value is: %x\n",sum)
}


func TestSM2(t *testing.T) {
priv, err := sm2.GenerateKey(rand.Reader) // 生成密钥对
if err != nil {
log.Fatal(err)
}
msg := []byte("Tongji Fintech Research Institute")
pub := &priv.PublicKey
ciphertxt, err := pub.EncryptAsn1(msg,rand.Reader) //sm2加密
if err != nil {
log.Fatal(err)
}
fmt.Printf("加密结果:%x\n",ciphertxt)
plaintxt,err := priv.DecryptAsn1(ciphertxt) //sm2解密
if err != nil {
log.Fatal(err)
}
if !bytes.Equal(msg,plaintxt){
log.Fatal("原文不匹配")
}

sign,err := priv.Sign(rand.Reader, msg, nil) //sm2签名
if err != nil {
log.Fatal(err)
}
isok := pub.Verify(msg, sign) //sm2验签
fmt.Printf("Verified: %v\n", isok)
}

func TestX509(t *testing.T) {
priv, err := sm2.GenerateKey(nil) // 生成密钥对
if err != nil {
t.Fatal(err)
}
privPem, err := WritePrivateKeyToPem(priv, nil) // 生成密钥文件
if err != nil {
t.Fatal(err)
}
pubKey, _ := priv.Public().(*sm2.PublicKey)
pubkeyPem, err := WritePublicKeyToPem(pubKey) // 生成公钥文件
privKey, err := ReadPrivateKeyFromPem(privPem, nil) // 读取密钥
if err != nil {
t.Fatal(err)
}
pubKey, err = ReadPublicKeyFromPem(pubkeyPem) // 读取公钥
if err != nil {
t.Fatal(err)
}
templateReq := CertificateRequest{
Subject: pkix.Name{
CommonName: "test.example.com",
Organization: []string{"Test"},
},
// SignatureAlgorithm: ECDSAWithSHA256,
SignatureAlgorithm: SM2WithSM3,
}
reqPem, err := CreateCertificateRequestToPem(&templateReq, privKey)
if err != nil {
t.Fatal(err)
}
req, err := ReadCertificateRequestFromPem(reqPem)
if err != nil {
t.Fatal(err)
}
err = req.CheckSignature()
if err != nil {
t.Fatalf("Request CheckSignature error:%v", err)
} else {
fmt.Printf("CheckSignature ok\n")
}

testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
extraExtensionData := []byte("extra extension")
commonName := "test.example.com"
template := Certificate{
// SerialNumber is negative to ensure that negative
// values are parsed. This is due to the prevalence of
// buggy code that produces certificates with negative
// serial numbers.
SerialNumber: big.NewInt(-1),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{"TEST"},
Country: []string{"China"},
ExtraNames: []pkix.AttributeTypeAndValue{
{
Type: []int{2, 5, 4, 42},
Value: "Gopher",
},
// This should override the Country, above.
{
Type: []int{2, 5, 4, 6},
Value: "NL",
},
},
},
NotBefore: time.Now(),
NotAfter: time.Date(2021, time.October, 10, 12, 1, 1, 1, time.UTC),

// SignatureAlgorithm: ECDSAWithSHA256,
SignatureAlgorithm: SM2WithSM3,

SubjectKeyId: []byte{1, 2, 3, 4},
KeyUsage: KeyUsageCertSign,

ExtKeyUsage: testExtKeyUsage,
UnknownExtKeyUsage: testUnknownExtKeyUsage,

BasicConstraintsValid: true,
IsCA: true,

OCSPServer: []string{"http://ocsp.example.com"},
IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"},

DNSNames: []string{"test.example.com"},
EmailAddresses: []string{"gopher@golang.org"},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},

PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
PermittedDNSDomains: []string{".example.com", "example.com"},

CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},

ExtraExtensions: []pkix.Extension{
{
Id: []int{1, 2, 3, 4},
Value: extraExtensionData,
},
},
}
pubKey, _ = priv.Public().(*sm2.PublicKey)
certpem, err := CreateCertificateToPem(&template, &template, pubKey, privKey)
if err != nil {
t.Fatal("failed to create cert file")
}

cert, err := ReadCertificateFromPem(certpem)
if err != nil {
t.Fatal("failed to read cert file")
}
err = cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("CheckSignature ok\n")
}
}

拓展

Fabric的国密扩展方法

一种是基于Fabric本身扩展国密包。这种改法不用对Golang标准库做任何地改动,所有的修改都在Fabric项目源码上进行;

其一,把国密的库进行移植,封装gm-crypto;

其二,扩展Fabric现有的bccsp模块;

其三,修改x509证书相关的地方。

Fabric-CA主要是为了实现对加入联盟链的成员的身份控制以及数据生成保管。Fabric-CA中,Lib,主要是接口的实现,主要在解析申请证书请求以及签发证书流程要替换为国密算法;Util,该包数据工具类,主要在证书的编解码等操作中扩展国密算法;Vendor中,替换对Fabric的包的引用,提供对国密算法的支持

在Fabric中扩展国密算法,大概有以下几个方面:第一,Fabric框架扩展支持国密算法;第二,Fabric-CA扩展支持国密算法;第三,Fabric-SDK扩展支持国密算法;第四,fabric-baseimage、fabric-baseos镜像扩展支持国密算法;第五,Fabirc框架扩展支持加载.so库。”

另一种方法是基于Golang标准库扩展国密。这种方案是直接扩充Golang标准库,改法更为清晰。

参考:

https://zhuanlan.zhihu.com/p/132352160

https://github.com/tjfoc/gmsm/blob/master/API%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md#go%E5%8C%85%E5%AE%89%E8%A3%85