2021-10-06 00:06:17 +00:00
|
|
|
package azurekms
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto"
|
|
|
|
"encoding/json"
|
2021-10-07 01:39:12 +00:00
|
|
|
"net/url"
|
2021-10-06 00:06:17 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/smallstep/certificates/kms/uri"
|
|
|
|
"go.step.sm/crypto/jose"
|
|
|
|
)
|
|
|
|
|
|
|
|
// defaultContext returns the default context used in requests to azure.
|
|
|
|
func defaultContext() (context.Context, context.CancelFunc) {
|
|
|
|
return context.WithTimeout(context.Background(), 15*time.Second)
|
|
|
|
}
|
|
|
|
|
2021-10-07 01:39:12 +00:00
|
|
|
// getKeyName returns the uri of the key vault key.
|
|
|
|
func getKeyName(vault, name string, bundle keyvault.KeyBundle) string {
|
|
|
|
if bundle.Key != nil && bundle.Key.Kid != nil {
|
|
|
|
sm := keyIDRegexp.FindAllStringSubmatch(*bundle.Key.Kid, 1)
|
|
|
|
if len(sm) == 1 && len(sm[0]) == 4 {
|
|
|
|
m := sm[0]
|
|
|
|
u := uri.New(Scheme, url.Values{
|
|
|
|
"vault": []string{m[1]},
|
|
|
|
"name": []string{m[2]},
|
|
|
|
})
|
|
|
|
u.RawQuery = url.Values{"version": []string{m[3]}}.Encode()
|
|
|
|
return u.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Fallback to URI without id.
|
|
|
|
return uri.New(Scheme, url.Values{
|
|
|
|
"vault": []string{vault},
|
|
|
|
"name": []string{name},
|
|
|
|
}).String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseKeyName returns the key vault, name and version from URIs like:
|
|
|
|
//
|
|
|
|
// - azurekms:vault=key-vault;name=key-name
|
|
|
|
// - azurekms:vault=key-vault;name=key-name;id=key-id
|
|
|
|
//
|
|
|
|
// The key-id defines the version of the key, if it is not passed the latest
|
|
|
|
// version will be used.
|
2021-10-06 00:06:17 +00:00
|
|
|
func parseKeyName(rawURI string) (vault, name, version string, err error) {
|
|
|
|
var u *uri.URI
|
|
|
|
|
|
|
|
u, err = uri.ParseWithScheme("azurekms", rawURI)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-10-07 01:39:12 +00:00
|
|
|
if name = u.Get("name"); name == "" {
|
|
|
|
err = errors.Errorf("key uri %s is not valid: name is missing", rawURI)
|
2021-10-06 00:06:17 +00:00
|
|
|
return
|
|
|
|
}
|
2021-10-07 01:39:12 +00:00
|
|
|
if vault = u.Get("vault"); vault == "" {
|
|
|
|
err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI)
|
|
|
|
name = ""
|
2021-10-06 00:06:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
version = u.Get("version")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func vaultBaseURL(vault string) string {
|
|
|
|
return "https://" + vault + ".vault.azure.net/"
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertKey(key *keyvault.JSONWebKey) (crypto.PublicKey, error) {
|
|
|
|
b, err := json.Marshal(key)
|
|
|
|
if err != nil {
|
2021-10-06 00:11:23 +00:00
|
|
|
return nil, errors.Wrap(err, "error marshaling key")
|
2021-10-06 00:06:17 +00:00
|
|
|
}
|
|
|
|
var jwk jose.JSONWebKey
|
|
|
|
if err := jwk.UnmarshalJSON(b); err != nil {
|
2021-10-06 00:11:23 +00:00
|
|
|
return nil, errors.Wrap(err, "error unmarshaling key")
|
2021-10-06 00:06:17 +00:00
|
|
|
}
|
|
|
|
return jwk.Key, nil
|
|
|
|
}
|