mirror of https://github.com/dapr/kit.git
98 lines
3.2 KiB
Go
98 lines
3.2 KiB
Go
/*
|
|
Copyright 2022 The Dapr Authors
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
|
)
|
|
|
|
// SerializeKey serializes a jwk.Key in the appropriate format so they can be wrapped.
|
|
// Symmetric keys are returned as raw bytes, while asymmetric keys are marshalled as ASN.1 DER (X.509, not PEM-encoded).
|
|
func SerializeKey(key jwk.Key) ([]byte, error) {
|
|
var rawKey any
|
|
err := key.Raw(&rawKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract raw key: %w", err)
|
|
}
|
|
|
|
switch r := rawKey.(type) {
|
|
case []byte: // Symmetric keys
|
|
return r, nil
|
|
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: // Private keys: marshal as PKCS#8
|
|
return x509.MarshalPKCS8PrivateKey(r)
|
|
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: // Public keys: marshal as PKIX
|
|
return x509.MarshalPKIXPublicKey(r)
|
|
default:
|
|
return nil, errors.New("unsupported key type")
|
|
}
|
|
}
|
|
|
|
// ParseKey takes a byte slice and returns a key (public, private, symmetric) after determining its type.
|
|
// It supports keys represented as JWKs, PEM-encoded (PKCS#8, PKCS#1 or PKIX) or as raw bytes (optionally base64-encoded).
|
|
// The parameter contentType is optional and it can contain a mime type.
|
|
func ParseKey(raw []byte, contentType string) (jwk.Key, error) {
|
|
l := len(raw)
|
|
if l == 0 {
|
|
return nil, errors.New("key is empty")
|
|
}
|
|
|
|
// Determine the type of key if the type parameter is set
|
|
switch contentType {
|
|
case "application/json": // JWK
|
|
return jwk.ParseKey(raw)
|
|
case "application/x-pem-file", "application/pkcs8": // Generic PEM: PKCS#1, PKCS#8, PKIX
|
|
return jwk.ParseKey(raw, jwk.WithPEM(true))
|
|
}
|
|
|
|
// Heuristically determine the type of key
|
|
switch {
|
|
case raw[0] == '{' && l != 16 && l != 24 && l != 32: // Assume it's a JWK unless the length is 16, 24, or 32 bytes
|
|
return jwk.ParseKey(raw)
|
|
case len(raw) > 10 && string(raw[0:5]) == ("-----"): // Assume it's something PEM-encoded
|
|
return jwk.ParseKey(raw, jwk.WithPEM(true))
|
|
default: // Assume a symmetric key
|
|
return parseSymmetricKey(raw)
|
|
}
|
|
}
|
|
|
|
func parseSymmetricKey(raw []byte) (jwk.Key, error) {
|
|
// Try parsing as base64; first: remove any padding if present
|
|
trimmedRaw := bytes.TrimRight(raw, "=")
|
|
|
|
// Try parsing as base64-standard
|
|
dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(raw)))
|
|
n, err := base64.RawStdEncoding.Decode(dst, trimmedRaw)
|
|
if err == nil {
|
|
return jwk.FromRaw(dst[:n])
|
|
}
|
|
|
|
// Try parsing as base64-url
|
|
n, err = base64.RawURLEncoding.Decode(dst, trimmedRaw)
|
|
if err == nil {
|
|
return jwk.FromRaw(dst[:n])
|
|
}
|
|
|
|
// Treat the byte slice as the raw, symmetric key
|
|
return jwk.FromRaw(raw)
|
|
}
|