1package main
2
3import (
4 "crypto/x509"
5 "encoding/json"
6 "encoding/pem"
7 "errors"
8 "fmt"
9 "log"
10 "os"
11
12 "github.com/go-jose/go-jose/v4"
13)
14
15// pemToJWK reads an SPKI public key (EC, RSA or Ed25519) and returns its JWK JSON.
16func pemToJWK(data []byte) ([]byte, error) {
17 block, _ := pem.Decode(data)
18 if block == nil || block.Type != "PUBLIC KEY" {
19 return nil, errors.New("no PUBLIC KEY block")
20 }
21 key, err := x509.ParsePKIXPublicKey(block.Bytes)
22 if err != nil {
23 return nil, err
24 }
25 return json.Marshal(jose.JSONWebKey{Key: key})
26}
27
28// jwkToPEM reads a public JWK and returns its SPKI PEM.
29func jwkToPEM(data []byte) ([]byte, error) {
30 var jwk jose.JSONWebKey
31 if err := jwk.UnmarshalJSON(data); err != nil {
32 return nil, err
33 }
34 if !jwk.IsPublic() {
35 return nil, errors.New("refusing to convert a private JWK")
36 }
37 der, err := x509.MarshalPKIXPublicKey(jwk.Key)
38 if err != nil {
39 return nil, err
40 }
41 return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}), nil
42}
43
44func main() {
45 for _, step := range []struct {
46 in, out string
47 convert func([]byte) ([]byte, error)
48 }{
49 {"pub.pem", "out.jwk.json", pemToJWK},
50 {"pub.jwk.json", "out.pem", jwkToPEM},
51 {"ed25519.pem", "", pemToJWK},
52 } {
53 data, err := os.ReadFile(step.in)
54 if err != nil {
55 log.Fatal(err)
56 }
57 result, err := step.convert(data)
58 if err != nil {
59 log.Fatalf("%s: %v", step.in, err)
60 }
61 fmt.Printf("%s\n", result)
62 if step.out != "" {
63 if err := os.WriteFile(step.out, result, 0o644); err != nil {
64 log.Fatal(err)
65 }
66 }
67 }
68}
69