-
Notifications
You must be signed in to change notification settings - Fork 61
/
pkcs8.go
380 lines (336 loc) · 11.6 KB
/
pkcs8.go
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// Package pkcs8 implements functions to parse and convert private keys in PKCS#8 format with ShangMi(SM) support, as defined in RFC5208 and RFC5958.
package pkcs8
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"hash"
"strconv"
"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm9"
"github.com/emmansun/gmsm/smx509"
)
// Hash identifies a cryptographic hash function that is implemented in another
// package.
type Hash uint
const (
SHA1 Hash = 1 + iota
SHA224
SHA256
SHA384
SHA512
SHA512_224
SHA512_256
SM3
)
// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
switch h {
case SM3:
return sm3.New()
case SHA1:
return sha1.New()
case SHA224:
return sha256.New224()
case SHA256:
return sha256.New()
case SHA384:
return sha512.New384()
case SHA512:
return sha512.New()
case SHA512_224:
return sha512.New512_224()
case SHA512_256:
return sha512.New512_256()
}
panic("pkcs8: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}
// DefaultOpts are the default options for encrypting a key if none are given.
// The defaults can be changed by the library user.
var DefaultOpts = &Opts{
Cipher: pkcs.AES256CBC,
KDFOpts: PBKDF2Opts{
SaltSize: 8,
IterationCount: 10000,
HMACHash: SHA256,
},
}
// KDFOpts contains options for a key derivation function.
// An implementation of this interface must be specified when encrypting a PKCS#8 key.
type KDFOpts interface {
// DeriveKey derives a key of size bytes from the given password and salt.
// It returns the key and the ASN.1-encodable parameters used.
DeriveKey(password, salt []byte, size int) (key []byte, params KDFParameters, err error)
// GetSaltSize returns the salt size specified.
GetSaltSize() int
// OID returns the OID of the KDF specified.
OID() asn1.ObjectIdentifier
}
// KDFParameters contains parameters (salt, etc.) for a key deriviation function.
// It must be a ASN.1-decodable structure.
// An implementation of this interface is created when decoding an encrypted PKCS#8 key.
type KDFParameters interface {
// DeriveKey derives a key of size bytes from the given password.
// It uses the salt from the decoded parameters.
DeriveKey(password []byte, size int) (key []byte, err error)
}
var kdfs = make(map[string]func() KDFParameters)
// RegisterKDF registers a function that returns a new instance of the given KDF
// parameters. This allows the library to support client-provided KDFs.
func RegisterKDF(oid asn1.ObjectIdentifier, params func() KDFParameters) {
kdfs[oid.String()] = params
}
// for encrypted private-key information
type encryptedPrivateKeyInfo struct {
EncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedData []byte
}
// Opts contains options for encrypting a PKCS#8 key.
type Opts struct {
Cipher pkcs.Cipher
KDFOpts KDFOpts
}
// Unecrypted PKCS8
var (
oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
)
type pbes2Params struct {
KeyDerivationFunc pkix.AlgorithmIdentifier
EncryptionScheme pkix.AlgorithmIdentifier
}
func parseKeyDerivationFunc(keyDerivationFunc pkix.AlgorithmIdentifier) (KDFParameters, error) {
oid := keyDerivationFunc.Algorithm.String()
newParams, ok := kdfs[oid]
if !ok {
return nil, fmt.Errorf("pkcs8: unsupported KDF (OID: %s)", oid)
}
params := newParams()
_, err := asn1.Unmarshal(keyDerivationFunc.Parameters.FullBytes, params)
if err != nil {
return nil, errors.New("pkcs8: invalid KDF parameters")
}
return params, nil
}
// ParsePrivateKey parses a DER-encoded PKCS#8 private key.
// Password can be nil.
// This is equivalent to ParsePKCS8PrivateKey.
func ParsePrivateKey(der []byte, password []byte) (any, KDFParameters, error) {
// No password provided, assume the private key is unencrypted
if len(password) == 0 {
privateKey, err := smx509.ParsePKCS8PrivateKey(der)
return privateKey, nil, err
}
// Use the password provided to decrypt the private key
var privKey encryptedPrivateKeyInfo
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
if block, _ := pem.Decode(der); block != nil {
return nil, nil, errors.New("pkcs8: this method just supports DER-encoded key")
}
return nil, nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
}
if !privKey.EncryptionAlgorithm.Algorithm.Equal(oidPBES2) {
return nil, nil, errors.New("pkcs8: only PBES2 supported")
}
var params pbes2Params
if _, err := asn1.Unmarshal(privKey.EncryptionAlgorithm.Parameters.FullBytes, ¶ms); err != nil {
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
}
cipher, err := pkcs.GetCipher(params.EncryptionScheme)
if err != nil {
return nil, nil, err
}
kdfParams, err := parseKeyDerivationFunc(params.KeyDerivationFunc)
if err != nil {
return nil, nil, err
}
keySize := cipher.KeySize()
symkey, err := kdfParams.DeriveKey(password, keySize)
if err != nil {
return nil, nil, err
}
encryptedKey := privKey.EncryptedData
decryptedKey, err := cipher.Decrypt(symkey, ¶ms.EncryptionScheme.Parameters, encryptedKey)
if err != nil {
return nil, nil, err
}
key, err := smx509.ParsePKCS8PrivateKey(decryptedKey)
if err != nil {
return nil, nil, errors.New("pkcs8: incorrect password? failed to parse private key while ParsePKCS8PrivateKey: " + err.Error())
}
return key, kdfParams, nil
}
// MarshalPrivateKey encodes a private key into DER-encoded PKCS#8 with the given options.
// Password can be nil.
func MarshalPrivateKey(priv any, password []byte, opts *Opts) ([]byte, error) {
if len(password) == 0 {
return smx509.MarshalPKCS8PrivateKey(priv)
}
if opts == nil {
opts = DefaultOpts
}
// Convert private key into PKCS8 format
pkey, err := smx509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, err
}
encAlg := opts.Cipher
salt := make([]byte, opts.KDFOpts.GetSaltSize())
_, err = rand.Read(salt)
if err != nil {
return nil, err
}
key, kdfParams, err := opts.KDFOpts.DeriveKey(password, salt, encAlg.KeySize())
if err != nil {
return nil, err
}
encryptionScheme, encryptedKey, err := encAlg.Encrypt(key, pkey)
if err != nil {
return nil, err
}
marshalledParams, err := asn1.Marshal(kdfParams)
if err != nil {
return nil, err
}
keyDerivationFunc := pkix.AlgorithmIdentifier{
Algorithm: opts.KDFOpts.OID(),
Parameters: asn1.RawValue{FullBytes: marshalledParams},
}
encryptionAlgorithmParams := pbes2Params{
EncryptionScheme: *encryptionScheme,
KeyDerivationFunc: keyDerivationFunc,
}
marshalledEncryptionAlgorithmParams, err := asn1.Marshal(encryptionAlgorithmParams)
if err != nil {
return nil, err
}
encryptionAlgorithm := pkix.AlgorithmIdentifier{
Algorithm: oidPBES2,
Parameters: asn1.RawValue{FullBytes: marshalledEncryptionAlgorithmParams},
}
encryptedPkey := encryptedPrivateKeyInfo{
EncryptionAlgorithm: encryptionAlgorithm,
EncryptedData: encryptedKey,
}
return asn1.Marshal(encryptedPkey)
}
// ParsePKCS8PrivateKey parses encrypted/unencrypted private keys in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKey(der []byte, v ...[]byte) (any, error) {
var password []byte
if len(v) > 0 {
password = v[0]
}
privateKey, _, err := ParsePrivateKey(der, password)
return privateKey, err
}
// ParsePKCS8PrivateKeyRSA parses encrypted/unencrypted private keys in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKeyRSA(der []byte, v ...[]byte) (*rsa.PrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type RSA")
}
return typedKey, nil
}
// ParsePKCS8PrivateKeyECDSA parses encrypted/unencrypted private keys in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type ECDSA")
}
return typedKey, nil
}
// ParsePKCS8PrivateKeySM2 parses encrypted/unencrypted SM2 private key in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKeySM2(der []byte, v ...[]byte) (*sm2.PrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*sm2.PrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type SM2")
}
return typedKey, nil
}
// ParseSM9SignMasterPrivateKey parses encrypted/unencrypted SM9 sign master private key in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParseSM9SignMasterPrivateKey(der []byte, v ...[]byte) (*sm9.SignMasterPrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*sm9.SignMasterPrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type SM9 sign master private key")
}
return typedKey, nil
}
// ParseSM9SignPrivateKey parses encrypted/unencrypted SM9 sign private key in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParseSM9SignPrivateKey(der []byte, v ...[]byte) (*sm9.SignPrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*sm9.SignPrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type SM9 sign user private key")
}
return typedKey, nil
}
// ParseSM9EncryptMasterPrivateKey parses encrypted/unencrypted SM9 encrypt master private key in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParseSM9EncryptMasterPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptMasterPrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*sm9.EncryptMasterPrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type SM9 encrypt master private key")
}
return typedKey, nil
}
// ParseSM9EncryptPrivateKey parses encrypted/unencrypted SM9 encrypt private key in PKCS#8 format.
// To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParseSM9EncryptPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptPrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
return nil, err
}
typedKey, ok := key.(*sm9.EncryptPrivateKey)
if !ok {
return nil, errors.New("pkcs8: key block is not of type SM9 encrypt user private key")
}
return typedKey, nil
}
// ConvertPrivateKeyToPKCS8 converts the private key into PKCS#8 format.
// To encrypt the private key, the password of []byte type should be provided as the second parameter.
func ConvertPrivateKeyToPKCS8(priv any, v ...[]byte) ([]byte, error) {
var password []byte
if len(v) > 0 {
password = v[0]
}
return MarshalPrivateKey(priv, password, nil)
}