Skip to content

Commit ab7af08

Browse files
committed
Change to PKCS#8 format
1 parent a990890 commit ab7af08

2 files changed

Lines changed: 167 additions & 142 deletions

File tree

Lines changed: 153 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,153 @@
1-
package signing
2-
3-
import (
4-
"crypto/rand"
5-
"crypto/rsa"
6-
"crypto/sha256"
7-
"crypto/x509"
8-
"encoding/base64"
9-
"encoding/json"
10-
"encoding/pem"
11-
"fmt"
12-
"math/big"
13-
"net/http"
14-
"os"
15-
16-
log "github.com/sirupsen/logrus"
17-
)
18-
19-
var (
20-
rsaPrivateKey *rsa.PrivateKey
21-
myJWKS Jwks
22-
keyID string
23-
CredURI string
24-
)
25-
26-
// InitSigning initializes or loads the RSA key and attaches JWKS endpoint.
27-
// keyFilePath: path to store/load private key
28-
// mux: ServeMux to attach /.well-known/jwks.json
29-
func InitSigning(mux *http.ServeMux, keyFilePath string, extHost string) {
30-
loadOrGenerateKey(keyFilePath)
31-
32-
// Build JWKS from public key
33-
pubJwk, err := makeJWKFromRSAPrivateKey(keyID)
34-
if err != nil {
35-
log.WithError(err).Fatal("Failed to create JWK")
36-
}
37-
myJWKS = Jwks{Keys: []Jwk{pubJwk}}
38-
39-
// Expose JWKS endpoint
40-
mux.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
41-
log.Debug("Serving JWKS")
42-
w.Header().Set("Content-Type", "application/json")
43-
_ = json.NewEncoder(w).Encode(myJWKS)
44-
})
45-
46-
CredURI = "http://" + extHost + "/uma"
47-
}
48-
49-
// loadOrGenerateKey loads the RSA private key from disk or generates a new one.
50-
// It also generates a stable keyID based on public key hash.
51-
func loadOrGenerateKey(path string) {
52-
if _, err := os.Stat(path); err == nil {
53-
key, id := readPrivateKeyFromFile(path)
54-
rsaPrivateKey = key
55-
keyID = id
56-
log.Infof("Loaded RSA key with KID=%s", keyID)
57-
return
58-
}
59-
60-
// Generate new key
61-
key, err := rsa.GenerateKey(rand.Reader, 2048)
62-
if err != nil {
63-
log.WithError(err).Fatal("Failed to generate RSA key")
64-
}
65-
rsaPrivateKey = key
66-
67-
// Compute keyID from public key hash (stable)
68-
pubBytes := x509.MarshalPKCS1PublicKey(&rsaPrivateKey.PublicKey)
69-
hash := sha256.Sum256(pubBytes)
70-
keyID = fmt.Sprintf("%x", hash[:8]) // first 8 bytes of SHA256 hash
71-
72-
// Save key to disk
73-
savePrivateKeyToFile(key, path)
74-
log.Infof("Generated new RSA key with KID=%s", keyID)
75-
}
76-
77-
// readPrivateKeyFromFile loads a PEM-encoded RSA private key and computes KID
78-
func readPrivateKeyFromFile(path string) (*rsa.PrivateKey, string) {
79-
data, err := os.ReadFile(path)
80-
if err != nil {
81-
log.WithError(err).Fatal("Failed to read private key file")
82-
}
83-
block, _ := pem.Decode(data)
84-
if block == nil || block.Type != "RSA PRIVATE KEY" {
85-
log.Fatal("Invalid PEM block in private key file")
86-
}
87-
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
88-
if err != nil {
89-
log.WithError(err).Fatal("Failed to parse RSA private key")
90-
}
91-
92-
// Compute stable keyID
93-
pubBytes := x509.MarshalPKCS1PublicKey(&key.PublicKey)
94-
hash := sha256.Sum256(pubBytes)
95-
kid := fmt.Sprintf("%x", hash[:8])
96-
97-
return key, kid
98-
}
99-
100-
// savePrivateKeyToFile writes PEM-encoded RSA key to disk
101-
func savePrivateKeyToFile(key *rsa.PrivateKey, path string) {
102-
pemData := pem.EncodeToMemory(&pem.Block{
103-
Type: "RSA PRIVATE KEY",
104-
Bytes: x509.MarshalPKCS1PrivateKey(key),
105-
})
106-
if err := os.WriteFile(path, pemData, 0600); err != nil {
107-
log.WithError(err).Fatal("Failed to write private key to file")
108-
}
109-
}
110-
111-
// Jwk represents a JSON Web Key
112-
type Jwk struct {
113-
Kty string `json:"kty"`
114-
Kid string `json:"kid"`
115-
Use string `json:"use"`
116-
Alg string `json:"alg"`
117-
N string `json:"n"`
118-
E string `json:"e"`
119-
}
120-
121-
// Jwks represents a JSON Web Key Set
122-
type Jwks struct {
123-
Keys []Jwk `json:"keys"`
124-
}
125-
126-
// makeJWKFromRSAPrivateKey builds a JWK with public part of RSA key
127-
func makeJWKFromRSAPrivateKey(kid string) (Jwk, error) {
128-
pub := rsaPrivateKey.Public().(*rsa.PublicKey)
129-
n := base64.RawURLEncoding.EncodeToString(pub.N.Bytes())
130-
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pub.E)).Bytes())
131-
132-
return Jwk{
133-
Kty: "RSA",
134-
Kid: kid,
135-
Use: "sig",
136-
Alg: "RS256",
137-
N: n,
138-
E: e,
139-
}, nil
140-
}
1+
package signing
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"crypto/sha256"
7+
"crypto/x509"
8+
"encoding/base64"
9+
"encoding/json"
10+
"encoding/pem"
11+
"fmt"
12+
"math/big"
13+
"net/http"
14+
"os"
15+
16+
log "github.com/sirupsen/logrus"
17+
)
18+
19+
var (
20+
rsaPrivateKey *rsa.PrivateKey
21+
myJWKS Jwks
22+
keyID string
23+
CredURI string
24+
)
25+
26+
// InitSigning initializes or loads the RSA key and attaches JWKS endpoint.
27+
// keyFilePath: path to store/load private key
28+
// mux: ServeMux to attach /.well-known/jwks.json
29+
func InitSigning(mux *http.ServeMux, keyFilePath string, extHost string) {
30+
loadOrGenerateKey(keyFilePath)
31+
32+
// Build JWKS from public key
33+
pubJwk, err := makeJWKFromRSAPrivateKey(keyID)
34+
if err != nil {
35+
log.WithError(err).Fatal("Failed to create JWK")
36+
}
37+
myJWKS = Jwks{Keys: []Jwk{pubJwk}}
38+
39+
// Expose JWKS endpoint
40+
mux.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
41+
log.Debug("Serving JWKS")
42+
w.Header().Set("Content-Type", "application/json")
43+
_ = json.NewEncoder(w).Encode(myJWKS)
44+
})
45+
46+
CredURI = "http://" + extHost + "/uma"
47+
}
48+
49+
// loadOrGenerateKey loads the RSA private key from disk or generates a new one.
50+
// It also generates a stable keyID based on public key hash.
51+
func loadOrGenerateKey(path string) {
52+
if _, err := os.Stat(path); err == nil {
53+
key, id := readPrivateKeyFromFile(path)
54+
rsaPrivateKey = key
55+
keyID = id
56+
log.Infof("Loaded RSA key with KID=%s", keyID)
57+
return
58+
}
59+
60+
// Generate new key
61+
key, err := rsa.GenerateKey(rand.Reader, 2048)
62+
if err != nil {
63+
log.WithError(err).Fatal("Failed to generate RSA key")
64+
}
65+
rsaPrivateKey = key
66+
67+
// Compute keyID from public key hash (stable)
68+
pubBytes := x509.MarshalPKCS1PublicKey(&rsaPrivateKey.PublicKey)
69+
hash := sha256.Sum256(pubBytes)
70+
keyID = fmt.Sprintf("%x", hash[:8]) // first 8 bytes of SHA256 hash
71+
72+
// Save key to disk
73+
savePrivateKeyToFile(key, path)
74+
log.Infof("Generated new RSA key with KID=%s", keyID)
75+
}
76+
77+
// readPrivateKeyFromFile loads a PEM-encoded RSA private key and computes KID
78+
func readPrivateKeyFromFile(path string) (*rsa.PrivateKey, string) {
79+
data, err := os.ReadFile(path)
80+
if err != nil {
81+
log.WithError(err).Fatal("Failed to read private key file")
82+
}
83+
block, _ := pem.Decode(data)
84+
if block == nil {
85+
log.Fatal("No valid PEM block found in private key file")
86+
}
87+
88+
if block.Type != "PRIVATE KEY" {
89+
log.Fatalf("Expected PEM block type 'PRIVATE KEY' (PKCS#8), got '%s'", block.Type)
90+
}
91+
92+
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
93+
if err != nil {
94+
log.WithError(err).Fatal("Failed to parse PKCS#8 private key")
95+
}
96+
97+
key, ok := parsedKey.(*rsa.PrivateKey)
98+
if !ok {
99+
log.Fatal("PKCS#8 key is not an RSA private key")
100+
}
101+
102+
pubBytes := x509.MarshalPKCS1PublicKey(&key.PublicKey)
103+
hash := sha256.Sum256(pubBytes)
104+
kid := fmt.Sprintf("%x", hash[:8])
105+
106+
return key, kid
107+
}
108+
109+
// savePrivateKeyToFile writes PEM-encoded RSA key to disk in PKCS#8 format
110+
func savePrivateKeyToFile(key *rsa.PrivateKey, path string) {
111+
keyBytes, err := x509.MarshalPKCS8PrivateKey(key)
112+
if err != nil {
113+
log.WithError(err).Fatal("Failed to marshal private key to PKCS#8")
114+
}
115+
pemData := pem.EncodeToMemory(&pem.Block{
116+
Type: "PRIVATE KEY",
117+
Bytes: keyBytes,
118+
})
119+
if err := os.WriteFile(path, pemData, 0600); err != nil {
120+
log.WithError(err).Fatal("Failed to write private key to file")
121+
}
122+
}
123+
124+
// Jwk represents a JSON Web Key
125+
type Jwk struct {
126+
Kty string `json:"kty"`
127+
Kid string `json:"kid"`
128+
Use string `json:"use"`
129+
Alg string `json:"alg"`
130+
N string `json:"n"`
131+
E string `json:"e"`
132+
}
133+
134+
// Jwks represents a JSON Web Key Set
135+
type Jwks struct {
136+
Keys []Jwk `json:"keys"`
137+
}
138+
139+
// makeJWKFromRSAPrivateKey builds a JWK with public part of RSA key
140+
func makeJWKFromRSAPrivateKey(kid string) (Jwk, error) {
141+
pub := rsaPrivateKey.Public().(*rsa.PublicKey)
142+
n := base64.RawURLEncoding.EncodeToString(pub.N.Bytes())
143+
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pub.E)).Bytes())
144+
145+
return Jwk{
146+
Kty: "RSA",
147+
Kid: kid,
148+
Use: "sig",
149+
Alg: "RS256",
150+
N: n,
151+
E: e,
152+
}, nil
153+
}

makefile

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.PHONY: init kind-start kind-stop kind-dashboard \
22
containers-build containers-load containers-all \
3-
kind-generate-key-pair \
3+
kind-generate-key-pair generate-ingress-key \
44
kind-deploy kind-start-traefik kind-start-cleaner \
55
kind-clean clean kind-stop-traefik \
66
kind-undeploy stop \
@@ -13,7 +13,7 @@
1313
# ------------------------
1414

1515
# Initialize kind cluster, build/load containers, generate keys, start cleaner
16-
init: kind-start containers-all kind-generate-key-pair kind-start-cleaner
16+
init: kind-start containers-all kind-generate-key-pair generate-ingress-key kind-start-cleaner
1717

1818
# Start kind cluster
1919
kind-start:
@@ -73,6 +73,16 @@ kind-generate-key-pair:
7373
@echo "🗑️ Cleaning up generated key pair files..."
7474
@rm uma-proxy.crt uma-proxy.key
7575

76+
# Generate RSA private key for ingress-uma
77+
generate-ingress-key:
78+
@echo "🔑 Generating RSA private key for ingress-uma..."
79+
@if [ ! -f private_key.pem ]; then \
80+
openssl genrsa -out private_key.pem 2048; \
81+
echo "✅ Generated private_key.pem"; \
82+
else \
83+
echo "ℹ️ private_key.pem already exists, skipping generation"; \
84+
fi
85+
7686
# ------------------------
7787
# Container targets
7888
# ------------------------
@@ -269,6 +279,8 @@ kind-clean:
269279
@echo "🧹 Removing localhost entries..."
270280
@sudo sed -i.bak '/aggregator\.local/d' /etc/hosts || true
271281
@sudo sed -i.bak '/wsl\.local/d' /etc/hosts || true
282+
@echo "🗑️ Removing generated key files..."
283+
@rm -f private_key.pem
272284
@echo "✅ Cleanup complete"
273285

274286
# Clean everything and delete the entire kind cluster

0 commit comments

Comments
 (0)