@@ -18,14 +18,53 @@ package utils
18
18
19
19
import (
20
20
"crypto/ecdsa"
21
+ "crypto/elliptic"
21
22
"crypto/rand"
22
23
"crypto/rsa"
23
24
"crypto/x509"
25
+ "encoding/asn1"
24
26
"encoding/pem"
25
27
"errors"
26
28
"fmt"
27
29
)
28
30
31
+ // struct to hold info required for PKCS#8
32
+ type pkcs8Info struct {
33
+ Version int
34
+ PrivateKeyAlgorithm []asn1.ObjectIdentifier
35
+ PrivateKey []byte
36
+ }
37
+
38
+ type ecPrivateKey struct {
39
+ Version int
40
+ PrivateKey []byte
41
+ NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
42
+ PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
43
+ }
44
+
45
+ var (
46
+ oidNamedCurveP224 = asn1.ObjectIdentifier {1 , 3 , 132 , 0 , 33 }
47
+ oidNamedCurveP256 = asn1.ObjectIdentifier {1 , 2 , 840 , 10045 , 3 , 1 , 7 }
48
+ oidNamedCurveP384 = asn1.ObjectIdentifier {1 , 3 , 132 , 0 , 34 }
49
+ oidNamedCurveP521 = asn1.ObjectIdentifier {1 , 3 , 132 , 0 , 35 }
50
+ )
51
+
52
+ var oidPublicKeyECDSA = asn1.ObjectIdentifier {1 , 2 , 840 , 10045 , 2 , 1 }
53
+
54
+ func oidFromNamedCurve (curve elliptic.Curve ) (asn1.ObjectIdentifier , bool ) {
55
+ switch curve {
56
+ case elliptic .P224 ():
57
+ return oidNamedCurveP224 , true
58
+ case elliptic .P256 ():
59
+ return oidNamedCurveP256 , true
60
+ case elliptic .P384 ():
61
+ return oidNamedCurveP384 , true
62
+ case elliptic .P521 ():
63
+ return oidNamedCurveP521 , true
64
+ }
65
+ return nil , false
66
+ }
67
+
29
68
// PrivateKeyToDER marshals a private key to der
30
69
func PrivateKeyToDER (privateKey * ecdsa.PrivateKey ) ([]byte , error ) {
31
70
if privateKey == nil {
@@ -35,7 +74,9 @@ func PrivateKeyToDER(privateKey *ecdsa.PrivateKey) ([]byte, error) {
35
74
return x509 .MarshalECPrivateKey (privateKey )
36
75
}
37
76
38
- // PrivateKeyToPEM converts a private key to PEM
77
+ // PrivateKeyToPEM converts the private key to PEM format.
78
+ // EC private keys are converted to PKCS#8 format.
79
+ // RSA private keys are converted to PKCS#1 format.
39
80
func PrivateKeyToPEM (privateKey interface {}, pwd []byte ) ([]byte , error ) {
40
81
// Validate inputs
41
82
if len (pwd ) != 0 {
@@ -48,16 +89,42 @@ func PrivateKeyToPEM(privateKey interface{}, pwd []byte) ([]byte, error) {
48
89
return nil , errors .New ("Invalid ecdsa private key. It must be different from nil." )
49
90
}
50
91
51
- raw , err := x509 .MarshalECPrivateKey (k )
92
+ // get the oid for the curve
93
+ oidNamedCurve , ok := oidFromNamedCurve (k .Curve )
94
+ if ! ok {
95
+ return nil , errors .New ("unknown elliptic curve" )
96
+ }
97
+
98
+ // based on https://golang.org/src/crypto/x509/sec1.go
99
+ privateKeyBytes := k .D .Bytes ()
100
+ paddedPrivateKey := make ([]byte , (k .Curve .Params ().N .BitLen ()+ 7 )/ 8 )
101
+ copy (paddedPrivateKey [len (paddedPrivateKey )- len (privateKeyBytes ):], privateKeyBytes )
102
+ // omit NamedCurveOID for compatibility as it's optional
103
+ asn1Bytes , err := asn1 .Marshal (ecPrivateKey {
104
+ Version : 1 ,
105
+ PrivateKey : paddedPrivateKey ,
106
+ PublicKey : asn1.BitString {Bytes : elliptic .Marshal (k .Curve , k .X , k .Y )},
107
+ })
52
108
53
109
if err != nil {
54
- return nil , err
110
+ return nil , fmt . Errorf ( "error marshaling EC key to asn1 [%s]" , err )
55
111
}
56
112
113
+ var pkcs8Key pkcs8Info
114
+ pkcs8Key .Version = 1
115
+ pkcs8Key .PrivateKeyAlgorithm = make ([]asn1.ObjectIdentifier , 2 )
116
+ pkcs8Key .PrivateKeyAlgorithm [0 ] = oidPublicKeyECDSA
117
+ pkcs8Key .PrivateKeyAlgorithm [1 ] = oidNamedCurve
118
+ pkcs8Key .PrivateKey = asn1Bytes
119
+
120
+ pkcs8Bytes , err := asn1 .Marshal (pkcs8Key )
121
+ if err != nil {
122
+ return nil , fmt .Errorf ("error marshaling EC key to asn1 [%s]" , err )
123
+ }
57
124
return pem .EncodeToMemory (
58
125
& pem.Block {
59
- Type : "ECDSA PRIVATE KEY" ,
60
- Bytes : raw ,
126
+ Type : "PRIVATE KEY" ,
127
+ Bytes : pkcs8Bytes ,
61
128
},
62
129
), nil
63
130
case * rsa.PrivateKey :
@@ -94,7 +161,7 @@ func PrivateKeyToEncryptedPEM(privateKey interface{}, pwd []byte) ([]byte, error
94
161
95
162
block , err := x509 .EncryptPEMBlock (
96
163
rand .Reader ,
97
- "ECDSA PRIVATE KEY" ,
164
+ "PRIVATE KEY" ,
98
165
raw ,
99
166
pwd ,
100
167
x509 .PEMCipherAES256 )
@@ -241,7 +308,7 @@ func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) {
241
308
242
309
return pem .EncodeToMemory (
243
310
& pem.Block {
244
- Type : "ECDSA PUBLIC KEY" ,
311
+ Type : "PUBLIC KEY" ,
245
312
Bytes : PubASN1 ,
246
313
},
247
314
), nil
@@ -302,7 +369,7 @@ func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error)
302
369
303
370
block , err := x509 .EncryptPEMBlock (
304
371
rand .Reader ,
305
- "ECDSA PUBLIC KEY" ,
372
+ "PUBLIC KEY" ,
306
373
raw ,
307
374
pwd ,
308
375
x509 .PEMCipherAES256 )
0 commit comments