Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 9 additions & 31 deletions Dockerfile-kms-importer
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
FROM golang:1.22 AS base
ARG VERSION

ARG ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY
ARG ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY
ARG ISSUER_KMS_ETH_PROVIDER_AWS_REGION

WORKDIR /service
ENV GOBIN /service/bin

COPY go.mod .
COPY go.sum .
COPY ./cmd/kms_priv_key_importer/ ./cmd/kms_priv_key_importer/
COPY ./internal ./internal

COPY ./internal/config ./internal/config
COPY ./internal/kms ./internal/kms
COPY ./internal/log ./internal/log
COPY ./internal/providers ./internal/providers
COPY ./internal/common ./internal/common
COPY ./pkg/PKCS8DER ./pkg/PKCS8DER



RUN go install -buildvcs=false -ldflags "-X main.build=${VERSION}" ./cmd/...
RUN go install -buildvcs=false -ldflags "-X main.build=${VERSION}" ./cmd/kms_priv_key_importer/main.go

FROM alpine:latest
ARG ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY
ARG ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY
ARG ISSUER_KMS_ETH_PROVIDER_AWS_REGION

RUN apk add --no-cache libstdc++ gcompat libgomp
RUN apk add --update busybox>1.3.1-r0
RUN apk add --update openssl>3.1.4-r1
Expand All @@ -35,22 +30,5 @@ RUN apk add doas; \
echo 'permit nopass :wheel as root' >> /etc/doas.d/doas.conf;
RUN chmod g+rx,o+rx /

COPY --from=base ./service/bin/* ./
COPY --from=base ./service/cmd/kms_priv_key_importer/aws_kms_material_key_importer.sh ./aws_kms_material_key_importer.sh
RUN chmod +x ./aws_kms_material_key_importer.sh

RUN if [ -n "$ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY" ]; then \
aws configure set aws_access_key_id ${ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY} --profile privadoid; \
else \
echo "ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY is not set"; \
fi
RUN if [ -n "$ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY" ]; then \
aws configure set aws_secret_access_key ${ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY} --profile privadoid; \
else \
echo "ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY is not set"; \
fi
RUN if [ -n "$ISSUER_KMS_ETH_PROVIDER_AWS_REGION" ]; then \
aws configure set region ${ISSUER_KMS_ETH_PROVIDER_AWS_REGION} --profile privadoid; \
else \
echo "ISSUER_KMS_ETH_PROVIDER_AWS_REGION is not set"; \
fi
COPY --from=base ./service/bin/main /usr/local/bin/kms_priv_key_importer
RUN chmod +x /usr/local/bin/kms_priv_key_importer
44 changes: 9 additions & 35 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -214,46 +214,20 @@ lint-fix: $(BIN)/golangci-lint
import-private-key-to-kms:
ifeq ($(ISSUER_KMS_ETH_PROVIDER), aws-kms)
@echo ">>> importing private key to AWS KMS"
docker build --build-arg ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY=$(aws_access_key) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY=$(aws_secret_key) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_REGION=$(aws_region) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_URL=$(aws_endpoint) -t privadoid-kms-importer -f ./Dockerfile-kms-importer .
$(eval result = $(shell docker run -it -v ./.env-issuer:/.env-issuer \
--network issuer-network \
privadoid-kms-importer ./kms_priv_key_importer))
@echo "result: $(result)"
$(eval keyID = $(shell echo $(result) | grep "key created keyId=" | sed 's/.*keyId=//'))
@if [ -n "$(keyID)" ]; then \
docker run -it --rm -v ./.env-issuer:/.env-issuer --network issuer-network \
privadoid-kms-importer sh ./aws_kms_material_key_importer.sh $(private_key) $(keyID) privadoid $(aws_endpoint) ; \
else \
echo "something went wrong because keyID is empty"; \
fi
@go build -o kms_priv_key_importer cmd/kms_priv_key_importer/main.go
./kms_priv_key_importer --privateKey=$(private_key)
else ifeq ($(ISSUER_KMS_ETH_PROVIDER), aws-sm)
@echo ">>> importing private key to AWS Secrets Manager"
docker build --build-arg ISSUER_KMS_ETH_PROVIDER_AWS_ACCESS_KEY=$(aws_access_key) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_SECRET_KEY=$(aws_secret_key) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_REGION=$(aws_region) \
--build-arg ISSUER_KMS_ETH_PROVIDER_AWS_URL=$(aws_endpoint) -t privadoid-kms-importer -f ./Dockerfile-kms-importer .
$(eval result=$(shell docker run -it -v ./.env-issuer:/.env-issuer \
--network issuer-network \
privadoid-kms-importer ./kms_priv_key_importer --privateKey=$(private_key)))
@echo "$(result)"
@go build -o kms_priv_key_importer cmd/kms_priv_key_importer/main.go
./kms_priv_key_importer --privateKey=$(private_key)
else ifeq ($(ISSUER_KMS_ETH_PROVIDER), localstorage)
echo ">>> importing private key to LOCALSTORAGE"
@docker build -t privadoid-kms-importer -f ./Dockerfile-kms-importer .
@if [ ! -f "$(ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH)/kms_localstorage_keys.json" ]; then \
mkdir -p $(ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH); \
touch $(ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH)/kms_localstorage_keys.json; \
echo "[]" > $(ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH)/kms_localstorage_keys.json; \
fi
docker run --rm -it -v ./.env-issuer:/.env-issuer -v $(ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH)/kms_localstorage_keys.json:/localstoragekeys/kms_localstorage_keys.json \
privadoid-kms-importer ./kms_priv_key_importer --privateKey=$(private_key)
@go build -o kms_priv_key_importer cmd/kms_priv_key_importer/main.go
./kms_priv_key_importer --privateKey=$(private_key)
else ifeq ($(ISSUER_KMS_ETH_PROVIDER), vault)
@echo ">>> importing private key to VAULT"
@docker build -t privadoid-kms-importer -f ./Dockerfile-kms-importer .
docker run --rm -it -v ./.env-issuer:/.env-issuer --network issuer-network \
privadoid-kms-importer ./kms_priv_key_importer --privateKey=$(private_key)
echo ">>> importing private key to Vault"
@go build -o kms_priv_key_importer cmd/kms_priv_key_importer/main.go
./kms_priv_key_importer --privateKey=$(private_key)
else
@echo "ISSUER_KMS_ETH_PROVIDER is not set"
endif
Expand Down
95 changes: 82 additions & 13 deletions cmd/kms_priv_key_importer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package main

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"flag"
Expand All @@ -20,13 +25,15 @@ import (
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
vault "github.com/hashicorp/vault/api"
"github.com/joho/godotenv"

"github.com/polygonid/sh-id-platform/internal/config"
"github.com/polygonid/sh-id-platform/internal/kms"
"github.com/polygonid/sh-id-platform/internal/log"
"github.com/polygonid/sh-id-platform/internal/providers"
"github.com/polygonid/sh-id-platform/pkg/PKCS8DER"
)

const (
Expand Down Expand Up @@ -90,17 +97,17 @@ func main() {
material[jsonKeyPath] = issuerPublishKeyPathVar
material[jsonKeyType] = ethereum

if issuerKMSETHProviderToUse == config.LocalStorage {
switch issuerKMSETHProviderToUse {
case config.LocalStorage:
material[jsonPrivateKey] = *fPrivateKey
if err := saveKeyMaterialToFile(ctx, issuerKmsPluginLocalStorageFilePath, kms.LocalStorageFileName, material); err != nil {
log.Error(ctx, "cannot save key material to file", "err", err)
return
}
log.Info(ctx, "private key saved to file:", "path:", kms.LocalStorageFileName)
return
}

if issuerKMSETHProviderToUse == config.Vault {
case config.Vault:
var vaultCli *vault.Client
var vaultErr error
vaultTokenVar := os.Getenv(issuerKeyStoreToken)
Expand Down Expand Up @@ -150,9 +157,8 @@ func main() {

log.Info(ctx, "private key saved to vault:", "path:", issuerPublishKeyPathVar)
return
}

if issuerKMSETHProviderToUse == config.AWSSM {
case config.AWSSM:
awsAccessKey := os.Getenv(awsAccessKey)
awsSecretKey := os.Getenv(awsSecretKey)
awsRegion := os.Getenv(awsRegion)
Expand Down Expand Up @@ -200,9 +206,8 @@ func main() {
}
log.Info(ctx, "private key saved to aws:", "path:", issuerPublishKeyPathVar)
return
}

if issuerKMSETHProviderToUse == config.AWSKMS {
case config.AWSKMS:
awsAccessKey := os.Getenv(awsAccessKey)
awsSecretKey := os.Getenv(awsSecretKey)
awsRegion := os.Getenv(awsRegion)
Expand All @@ -213,13 +218,17 @@ func main() {
return
}

keyId, err := createEmptyKey(ctx, awsAccessKey, awsSecretKey, awsRegion, awsURLEndpoint, issuerPublishKeyPathVar)
keyId, err := createAWSKMSKey(ctx, *fPrivateKey, awsAccessKey, awsSecretKey, awsRegion, awsURLEndpoint, issuerPublishKeyPathVar)
if err != nil {
log.Error(ctx, "cannot create empty key", "err", err)
return
}
log.Info(ctx, "key created", "keyId", *keyId)
return

default:
log.Error(ctx, "kms eth provider is invalid, supported values are: localstorage, vault, aws-sm and aws-kms")
return
}
}

Expand Down Expand Up @@ -253,9 +262,13 @@ func validate(issuerKMSETHProviderToUse string, fPrivateKey *string, ctx context
return nil
}

// createAWSKMSKey creates a new AWS KMS key with the provided private key and alias.
// It imports the private key material into the KMS key and creates an alias for it.
//
//nolint:unused
func createEmptyKey(ctx context.Context, awsAccessKey, awsSecretKey, awsRegion string, awsURL string, privateKeyAlias string) (*string, error) {
func createAWSKMSKey(ctx context.Context, privateKey string, awsAccessKey, awsSecretKey, awsRegion string, awsURL string, privateKeyAlias string) (*string, error) {
alias := "alias/" + privateKeyAlias

cfg, err := awsconfig.LoadDefaultConfig(
ctx,
awsconfig.WithRegion(awsRegion),
Expand All @@ -275,23 +288,79 @@ func createEmptyKey(ctx context.Context, awsAccessKey, awsSecretKey, awsRegion s
}

svc := awskms.NewFromConfig(cfg, options...)

// Check if alias exists
listAliasesInput := &awskms.ListAliasesInput{}
aliases, err := svc.ListAliases(ctx, listAliasesInput)
if err != nil {
log.Error(ctx, "cannot list aliases", "err", err)
return nil, err
}
for _, a := range aliases.Aliases {
if a.AliasName != nil && *a.AliasName == alias {
return nil, fmt.Errorf("alias %s already exists", alias)
}
}

privBytes, err := hex.DecodeString(privateKey)
if err != nil {
return nil, fmt.Errorf("error decoding private key: %w", err)
}

privKey, err := crypto.ToECDSA(privBytes)
if err != nil {
return nil, fmt.Errorf("error converting private key to ECDSA: %w", err)
}

privKey.Curve = secp256k1.S256()
der, err := PKCS8DER.MarshalECPrivateKeyToPKCS8DER(privKey)
if err != nil {
return nil, fmt.Errorf("error marshaling private key to PKCS8 DER: %w", err)
}

input := &awskms.CreateKeyInput{
KeySpec: types.KeySpecEccSecgP256k1,
KeyUsage: types.KeyUsageTypeSignVerify,
Origin: types.OriginTypeExternal,
Description: aws.String("imported key"),
}

result, err := svc.CreateKey(ctx, input)
createOutput, err := svc.CreateKey(ctx, input)
if err != nil {
log.Error(ctx, "cannot create key", "err", err)
return nil, err
}
keyID := *createOutput.KeyMetadata.KeyId
params, err := svc.GetParametersForImport(ctx, &awskms.GetParametersForImportInput{
KeyId: aws.String(keyID),
WrappingAlgorithm: types.AlgorithmSpecRsaesOaepSha256,
WrappingKeySpec: types.WrappingKeySpecRsa2048,
})
if err != nil {
log.Error(ctx, "cannot get parameters for import", "err", err)
return nil, err
}

rsaPubKey, _ := x509.ParsePKIXPublicKey(params.PublicKey)
encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaPubKey.(*rsa.PublicKey), der, nil)
if err != nil {
log.Error(ctx, "cannot encrypt key material", "err", err)
return nil, err
}
_, err = svc.ImportKeyMaterial(ctx, &awskms.ImportKeyMaterialInput{
KeyId: aws.String(keyID),
ImportToken: params.ImportToken,
EncryptedKeyMaterial: encryptedKey,
ExpirationModel: types.ExpirationModelTypeKeyMaterialDoesNotExpire,
})
if err != nil {
log.Error(ctx, "cannot import key material", "err", err)
return nil, err
}

alias := "alias/" + privateKeyAlias
inputAlias := &awskms.CreateAliasInput{
AliasName: aws.String(alias),
TargetKeyId: result.KeyMetadata.Arn,
TargetKeyId: createOutput.KeyMetadata.Arn,
}

_, err = svc.CreateAlias(ctx, inputAlias)
Expand All @@ -300,7 +369,7 @@ func createEmptyKey(ctx context.Context, awsAccessKey, awsSecretKey, awsRegion s
}

log.Info(ctx, "alias created:", "alias:", alias)
return result.KeyMetadata.KeyId, nil
return createOutput.KeyMetadata.KeyId, nil
}

func saveKeyMaterialToFile(ctx context.Context, folderPath, file string, keyMaterial map[string]string) error {
Expand Down
Loading