Skip to content

Commit ff1753d

Browse files
committed
feat: add helper methods for key formatting and conversion
Signed-off-by: Pierre-Henri Symoneaux <[email protected]>
1 parent 6a27345 commit ff1753d

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

objects.go

+80
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package kmip
22

33
import (
4+
"crypto"
45
"crypto/ecdsa"
56
"crypto/elliptic"
67
"crypto/rsa"
78
"crypto/x509"
9+
"encoding/pem"
810
"errors"
911
"fmt"
1012
"math/big"
@@ -72,6 +74,20 @@ func (sd *Certificate) X509Certificate() (*x509.Certificate, error) {
7274
return x509.ParseCertificate(sd.CertificateValue)
7375
}
7476

77+
// PemCertificate returns the PEM encoded value of an x509 certificate. It returns an error
78+
// if the kmip object is not a certificate of type X509, or if the certificate data is invalid.
79+
func (sd *Certificate) PemCertificate() (string, error) {
80+
cert, err := sd.X509Certificate()
81+
if err != nil {
82+
return "", err
83+
}
84+
block := pem.Block{
85+
Type: "CERTIFICATE",
86+
Bytes: cert.Raw,
87+
}
88+
return string(pem.EncodeToMemory(&block)), nil
89+
}
90+
7591
type SymmetricKey struct {
7692
KeyBlock KeyBlock
7793
}
@@ -227,6 +243,39 @@ func (key *PublicKey) ECDSA() (*ecdsa.PublicKey, error) {
227243
}
228244
}
229245

246+
// CryptoPublicKey parses and return the public key object into a go [crypto.PublicKey] object.
247+
func (key *PublicKey) CryptoPublicKey() (crypto.PublicKey, error) {
248+
switch key.KeyBlock.KeyFormatType {
249+
case KeyFormatTransparentECPublicKey, KeyFormatTransparentECDSAPublicKey:
250+
return key.ECDSA()
251+
case KeyFormatPKCS_1, KeyFormatTransparentRSAPublicKey:
252+
return key.RSA()
253+
case KeyFormatX_509:
254+
raw, err := key.KeyBlock.GetBytes()
255+
if err != nil {
256+
return nil, err
257+
}
258+
return x509.ParsePKIXPublicKey(raw)
259+
default:
260+
return nil, errors.New("Unsupported key format")
261+
}
262+
}
263+
264+
// PkixPem format the public key value into a PEM encoding of its PKIX, ASN.1 DER form.
265+
// The encoded public key is a SubjectPublicKeyInfo structure
266+
// (see RFC 5280, Section 4.1).
267+
func (key *PublicKey) PkixPem() (string, error) {
268+
pubkey, err := key.CryptoPublicKey()
269+
if err != nil {
270+
return "", err
271+
}
272+
bytes, err := x509.MarshalPKIXPublicKey(pubkey)
273+
if err != nil {
274+
return "", err
275+
}
276+
return string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: bytes})), nil
277+
}
278+
230279
type PrivateKey struct {
231280
KeyBlock KeyBlock
232281
}
@@ -365,6 +414,37 @@ func (key *PrivateKey) ECDSA() (*ecdsa.PrivateKey, error) {
365414
}
366415
}
367416

417+
// CryptoPrivateKey parses and return the private key object into a go [crypto.PrivateKey] object.
418+
func (key *PrivateKey) CryptoPrivateKey() (crypto.PrivateKey, error) {
419+
switch key.KeyBlock.KeyFormatType {
420+
case KeyFormatECPrivateKey, KeyFormatTransparentECPrivateKey, KeyFormatTransparentECDSAPrivateKey:
421+
return key.ECDSA()
422+
case KeyFormatPKCS_1, KeyFormatTransparentRSAPrivateKey:
423+
return key.RSA()
424+
case KeyFormatPKCS_8:
425+
raw, err := key.KeyBlock.GetBytes()
426+
if err != nil {
427+
return nil, err
428+
}
429+
return x509.ParsePKCS8PrivateKey(raw)
430+
default:
431+
return nil, errors.New("Unsupported key format")
432+
}
433+
}
434+
435+
// Pkcs8Pem format the private key into the PEM encoding of its PKCS #8, ASN.1 DER form.
436+
func (key *PrivateKey) Pkcs8Pem() (string, error) {
437+
privkey, err := key.CryptoPrivateKey()
438+
if err != nil {
439+
return "", err
440+
}
441+
bytes, err := x509.MarshalPKCS8PrivateKey(privkey)
442+
if err != nil {
443+
return "", err
444+
}
445+
return string(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: bytes})), nil
446+
}
447+
368448
type KeyBlock struct {
369449
KeyFormatType KeyFormatType
370450
KeyCompressionType *KeyCompressionType

objects_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"crypto/rand"
77
"crypto/rsa"
88
"crypto/x509"
9+
"encoding/pem"
910
"math/big"
1011
"net"
1112
"testing"
@@ -82,20 +83,32 @@ func TestCertificate_X509Certificate(t *testing.T) {
8283
xcert, err := cert.X509Certificate()
8384
require.NoError(t, err)
8485
require.EqualValues(t, caTpl.SerialNumber, xcert.SerialNumber)
86+
require.EqualValues(t, der, xcert.Raw)
87+
pemData, err := cert.PemCertificate()
88+
require.NoError(t, err)
89+
pemBlock, _ := pem.Decode([]byte(pemData))
90+
require.Equal(t, "CERTIFICATE", pemBlock.Type)
91+
require.EqualValues(t, der, pemBlock.Bytes)
8592
})
8693

8794
t.Run("invalid-data", func(t *testing.T) {
8895
cert := Certificate{CertificateType: X_509, CertificateValue: []byte{1, 2, 3}}
8996
xcert, err := cert.X509Certificate()
9097
require.Error(t, err)
9198
require.Nil(t, xcert)
99+
pemData, err := cert.PemCertificate()
100+
require.Error(t, err)
101+
require.Empty(t, pemData)
92102
})
93103

94104
t.Run("invalid-type", func(t *testing.T) {
95105
cert := Certificate{CertificateType: PGP, CertificateValue: der}
96106
xcert, err := cert.X509Certificate()
97107
require.Error(t, err)
98108
require.Nil(t, xcert)
109+
pemData, err := cert.PemCertificate()
110+
require.Error(t, err)
111+
require.Empty(t, pemData)
99112
})
100113
}
101114

@@ -163,6 +176,9 @@ func TestPublicKey_RSA(t *testing.T) {
163176
pub, err := pkey.RSA()
164177
require.NoError(t, err)
165178
require.EqualValues(t, &rsaKey.PublicKey, pub)
179+
cryptoKey, err := pkey.CryptoPublicKey()
180+
require.NoError(t, err)
181+
require.EqualValues(t, rsaKey.Public(), cryptoKey)
166182
})
167183

168184
t.Run("spki", func(t *testing.T) {
@@ -176,6 +192,9 @@ func TestPublicKey_RSA(t *testing.T) {
176192
pub, err := pkey.RSA()
177193
require.NoError(t, err)
178194
require.EqualValues(t, &rsaKey.PublicKey, pub)
195+
cryptoKey, err := pkey.CryptoPublicKey()
196+
require.NoError(t, err)
197+
require.EqualValues(t, rsaKey.Public(), cryptoKey)
179198
})
180199

181200
t.Run("transparent", func(t *testing.T) {
@@ -189,6 +208,9 @@ func TestPublicKey_RSA(t *testing.T) {
189208
pub, err := pkey.RSA()
190209
require.NoError(t, err)
191210
require.EqualValues(t, &rsaKey.PublicKey, pub)
211+
cryptoKey, err := pkey.CryptoPublicKey()
212+
require.NoError(t, err)
213+
require.EqualValues(t, rsaKey.Public(), cryptoKey)
192214
})
193215
}
194216

@@ -205,6 +227,9 @@ func TestPublicKey_ECDSA(t *testing.T) {
205227
pub, err := pkey.ECDSA()
206228
require.NoError(t, err)
207229
require.EqualValues(t, &ecKey.PublicKey, pub)
230+
cryptoKey, err := pkey.CryptoPublicKey()
231+
require.NoError(t, err)
232+
require.EqualValues(t, ecKey.Public(), cryptoKey)
208233
})
209234

210235
t.Run("transparent-uncompressed", func(t *testing.T) {
@@ -221,6 +246,9 @@ func TestPublicKey_ECDSA(t *testing.T) {
221246
pub, err := pkey.ECDSA()
222247
require.NoError(t, err)
223248
require.EqualValues(t, &ecKey.PublicKey, pub)
249+
cryptoKey, err := pkey.CryptoPublicKey()
250+
require.NoError(t, err)
251+
require.EqualValues(t, ecKey.Public(), cryptoKey)
224252
})
225253

226254
t.Run("transparent-compressed-prime", func(t *testing.T) {
@@ -236,6 +264,9 @@ func TestPublicKey_ECDSA(t *testing.T) {
236264
pub, err := pkey.ECDSA()
237265
require.NoError(t, err)
238266
require.EqualValues(t, &ecKey.PublicKey, pub)
267+
cryptoKey, err := pkey.CryptoPublicKey()
268+
require.NoError(t, err)
269+
require.EqualValues(t, ecKey.Public(), cryptoKey)
239270
})
240271
}
241272

@@ -252,6 +283,9 @@ func TestPrivateKey_RSA(t *testing.T) {
252283
pub, err := pkey.RSA()
253284
require.NoError(t, err)
254285
require.EqualValues(t, rsaKey, pub)
286+
cryptoKey, err := pkey.CryptoPrivateKey()
287+
require.NoError(t, err)
288+
require.EqualValues(t, rsaKey, cryptoKey)
255289
})
256290

257291
t.Run("pkcs8", func(t *testing.T) {
@@ -265,6 +299,9 @@ func TestPrivateKey_RSA(t *testing.T) {
265299
pub, err := pkey.RSA()
266300
require.NoError(t, err)
267301
require.EqualValues(t, rsaKey, pub)
302+
cryptoKey, err := pkey.CryptoPrivateKey()
303+
require.NoError(t, err)
304+
require.EqualValues(t, rsaKey, cryptoKey)
268305
})
269306

270307
t.Run("transparent", func(t *testing.T) {
@@ -284,6 +321,9 @@ func TestPrivateKey_RSA(t *testing.T) {
284321
pub, err := pkey.RSA()
285322
require.NoError(t, err)
286323
require.EqualValues(t, rsaKey, pub)
324+
cryptoKey, err := pkey.CryptoPrivateKey()
325+
require.NoError(t, err)
326+
require.EqualValues(t, rsaKey, cryptoKey)
287327
})
288328
}
289329

@@ -300,6 +340,9 @@ func TestPrivateKey_ECDSA(t *testing.T) {
300340
pub, err := pkey.ECDSA()
301341
require.NoError(t, err)
302342
require.EqualValues(t, ecKey, pub)
343+
cryptoKey, err := pkey.CryptoPrivateKey()
344+
require.NoError(t, err)
345+
require.EqualValues(t, ecKey, cryptoKey)
303346
})
304347
t.Run("sec1", func(t *testing.T) {
305348
der, _ := x509.MarshalECPrivateKey(ecKey)
@@ -312,6 +355,9 @@ func TestPrivateKey_ECDSA(t *testing.T) {
312355
pub, err := pkey.ECDSA()
313356
require.NoError(t, err)
314357
require.EqualValues(t, ecKey, pub)
358+
cryptoKey, err := pkey.CryptoPrivateKey()
359+
require.NoError(t, err)
360+
require.EqualValues(t, ecKey, cryptoKey)
315361
})
316362

317363
t.Run("transparent", func(t *testing.T) {
@@ -325,5 +371,8 @@ func TestPrivateKey_ECDSA(t *testing.T) {
325371
pub, err := pkey.ECDSA()
326372
require.NoError(t, err)
327373
require.EqualValues(t, ecKey, pub)
374+
cryptoKey, err := pkey.CryptoPrivateKey()
375+
require.NoError(t, err)
376+
require.EqualValues(t, ecKey, cryptoKey)
328377
})
329378
}

payloads/get.go

+45
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package payloads
22

33
import (
4+
"crypto"
45
"crypto/ecdsa"
56
"crypto/rsa"
67
"crypto/x509"
@@ -108,6 +109,16 @@ func (pl *GetResponsePayload) X509Certificate() (*x509.Certificate, error) {
108109
return cert.X509Certificate()
109110
}
110111

112+
// PemCertificate returns the PEM encoded value of an x509 certificate. It returns an error
113+
// if the kmip object is not a certificate of type X509, or if the certificate data is invalid.
114+
func (pl *GetResponsePayload) PemCertificate() (string, error) {
115+
if pl.ObjectType != kmip.ObjectTypeCertificate {
116+
return "", fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypeCertificate))
117+
}
118+
cert := pl.Object.(*kmip.Certificate)
119+
return cert.PemCertificate()
120+
}
121+
111122
func (pl *GetResponsePayload) RsaPrivateKey() (*rsa.PrivateKey, error) {
112123
if pl.ObjectType != kmip.ObjectTypePrivateKey {
113124
return nil, fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePrivateKey))
@@ -124,6 +135,22 @@ func (pl *GetResponsePayload) EcdsaPrivateKey() (*ecdsa.PrivateKey, error) {
124135
return key.ECDSA()
125136
}
126137

138+
// PrivateKey parses and return the private key object into a go [crypto.PrivateKey] object.
139+
func (pl *GetResponsePayload) PrivateKey() (crypto.PrivateKey, error) {
140+
if pl.ObjectType != kmip.ObjectTypePrivateKey {
141+
return nil, fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePrivateKey))
142+
}
143+
return pl.Object.(*kmip.PrivateKey).CryptoPrivateKey()
144+
}
145+
146+
// PemPrivateKey format the private key into the PEM encoding of its PKCS #8, ASN.1 DER form.
147+
func (pl *GetResponsePayload) PemPrivateKey() (string, error) {
148+
if pl.ObjectType != kmip.ObjectTypePrivateKey {
149+
return "", fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePrivateKey))
150+
}
151+
return pl.Object.(*kmip.PrivateKey).Pkcs8Pem()
152+
}
153+
127154
func (pl *GetResponsePayload) RsaPublicKey() (*rsa.PublicKey, error) {
128155
if pl.ObjectType != kmip.ObjectTypePublicKey {
129156
return nil, fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePublicKey))
@@ -139,3 +166,21 @@ func (pl *GetResponsePayload) EcdsaPublicKey() (*ecdsa.PublicKey, error) {
139166
key := pl.Object.(*kmip.PublicKey)
140167
return key.ECDSA()
141168
}
169+
170+
// PublicKey parses and return the public key object into a go [crypto.PublicKey] object.
171+
func (pl *GetResponsePayload) PublicKey() (crypto.PublicKey, error) {
172+
if pl.ObjectType != kmip.ObjectTypePublicKey {
173+
return nil, fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePublicKey))
174+
}
175+
return pl.Object.(*kmip.PublicKey).CryptoPublicKey()
176+
}
177+
178+
// PemPublicKey format the public key value into a PEM encoding of its PKIX, ASN.1 DER form.
179+
// The encoded public key is a SubjectPublicKeyInfo structure
180+
// (see RFC 5280, Section 4.1).
181+
func (pl *GetResponsePayload) PemPublicKey() (string, error) {
182+
if pl.ObjectType != kmip.ObjectTypePublicKey {
183+
return "", fmt.Errorf("Invalid object type. Got %s but want %s", ttlv.EnumStr(pl.ObjectType), ttlv.EnumStr(kmip.ObjectTypePublicKey))
184+
}
185+
return pl.Object.(*kmip.PublicKey).PkixPem()
186+
}

0 commit comments

Comments
 (0)