commit 02ed990f62b9a35e792e63a142e11338f6f20aa5 Author: William Petit Date: Tue Aug 19 17:13:18 2025 +0200 feat: initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7447f89 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/bin \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..df21cc9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.24 AS build + +COPY . /src + +WORKDIR /src + +RUN CGO_ENABLED=0 go build -o ./bin/jwk2pem . + +FROM busybox:latest AS runtime + +COPY --from=build /src/bin/jwk2pem /usr/bin/jwk2pem + +LABEL org.opencontainers.image.source=https://forge.cadoles.com/wpetit/jwk2pem + +CMD ["/usr/bin/jwk2pem"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b2df44d --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +DOCKER_IMAGE_REPOSITORY ?= reg.cadoles.com/cadoles/jwk2pem +DOCKER_IMAGE_TAG ?= latest + +build: + CGO_ENABLED=0 go build -o ./bin/jwk2pem ./ + +docker-build: + docker build \ + -t $(DOCKER_IMAGE_REPOSITORY):$(DOCKER_IMAGE_TAG) \ + . + +docker-release: docker-build + docker push $(DOCKER_IMAGE_REPOSITORY):$(DOCKER_IMAGE_TAG) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d7aaaa --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# `jwk2pem` + +Un simple outil en ligne de commande permettant de transformer une clé publique au format [JWK](https://www.rfc-editor.org/rfc/rfc7517) en sa représentation au format [PEM](https://fr.wikipedia.org/wiki/Demande_de_signature_de_certificat). + +## Usage + +```bash +echo | jwk2pem > mykey.pem +``` + +Une image Docker est également disponible `reg.cadoles.com/cadoles/jwk2pem`. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bb9d4a6 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module forge.cadoles.com/wpetit/jwk2pem + +go 1.24.6 + +require github.com/lestrrat-go/jwx/v2 v2.1.6 + +require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/lestrrat-go/blackmagic v1.0.3 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.6 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/segmentio/asm v1.2.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/sys v0.31.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c54b1fa --- /dev/null +++ b/go.sum @@ -0,0 +1,36 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs= +github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= +github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..1f9dbea --- /dev/null +++ b/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "encoding/pem" + "fmt" + "io" + "log" + "os" + + "github.com/lestrrat-go/jwx/v2/jwk" +) + +func main() { + // Read the JWK from standard input. + jwkBytes, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("failed to read from stdin: %s", err) + } + + // Parse the JWK. + key, err := jwk.ParseKey(jwkBytes) + if err != nil { + log.Fatalf("failed to parse JWK: %s", err) + } + + // Get the public key. + var rawPublicKey interface{} + if err := key.Raw(&rawPublicKey); err != nil { + log.Fatalf("failed to get raw public key: %s", err) + } + + // Encode the public key to PEM format. [4] + pemBytes, err := jwk.EncodePEM(key) + if err != nil { + log.Fatalf("failed to encode public key to PEM: %s", err) + } + + // Write the PEM encoded public key to standard output. + block, _ := pem.Decode(pemBytes) + if block == nil { + log.Fatal("failed to decode PEM block") + } + + if err := pem.Encode(os.Stdout, block); err != nil { + log.Fatalf("failed to write PEM block to stdout: %s", err) + } + + fmt.Println() +}