Skip to content

Commit

Permalink
ARO-4373 generate keypair and oidc docs for miwi clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdeepc2792 committed May 7, 2024
1 parent e9cdd84 commit dafefa0
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 0 deletions.
136 changes: 136 additions & 0 deletions pkg/util/oidcbuilder/jwks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package oidcbuilder

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"

"github.com/pkg/errors"
"gopkg.in/square/go-jose.v2"
)

func CreateKeyPair() (encPrivateKey []byte, encPublicKey []byte, err error) {
bitSize := 4096

// Generate RSA keypair
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to generate private key")
}
encodedPrivateKey := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})

// Generate public key from private keypair
pubKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to generate public key from private")
}
encodedPublicKey := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Headers: nil,
Bytes: pubKeyBytes,
})

return encodedPrivateKey, encodedPublicKey, nil
}

type JSONWebKeySet struct {
Keys []jose.JSONWebKey `json:"keys"`
}

// buildJSONWebKeySet builds JSON web key set from the public key
func BuildJSONWebKeySet(publicKeyContent []byte) ([]byte, error) {
block, _ := pem.Decode(publicKeyContent)
if block == nil {
return nil, errors.Errorf("Failed to decode PEM file")
}

publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, errors.Wrapf(err, "Failed to parse key content")
}

var alg jose.SignatureAlgorithm
switch publicKey.(type) {
case *rsa.PublicKey:
alg = jose.RS256
default:
return nil, errors.Errorf("Public key is not of type RSA")
}

kid, err := keyIDFromPublicKey(publicKey)
if err != nil {
return nil, errors.Wrapf(err, "Failed to fetch key ID from public key")
}

var keys []jose.JSONWebKey
keys = append(keys, jose.JSONWebKey{
Key: publicKey,
KeyID: kid,
Algorithm: string(alg),
Use: "sig",
})

keySet, err := json.MarshalIndent(JSONWebKeySet{Keys: keys}, "", " ")
if err != nil {
return nil, errors.Wrapf(err, "JSON encoding of web key set failed")
}

return keySet, nil
}

// keyIDFromPublicKey derives a key ID non-reversibly from a public key
// reference: https://github.com/kubernetes/kubernetes/blob/v1.21.0/pkg/serviceaccount/jwt.go#L89-L111
func keyIDFromPublicKey(publicKey interface{}) (string, error) {
publicKeyDERBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return "", errors.Wrapf(err, "Failed to serialize public key to DER format")
}

hasher := crypto.SHA256.New()
hasher.Write(publicKeyDERBytes)
publicKeyDERHash := hasher.Sum(nil)

keyID := base64.RawURLEncoding.EncodeToString(publicKeyDERHash)

return keyID, nil
}

const (
discoveryDocumentTemplate = `{
"issuer": "%s",
"jwks_uri": "%s/openid/v1/jwks",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"claims_supported": [
"aud",
"exp",
"sub",
"iat",
"iss",
"sub"
]
}`
)

func GenerateDiscoveryDocument(bucketURL string) string {
return fmt.Sprintf(discoveryDocumentTemplate, bucketURL, bucketURL)
}
72 changes: 72 additions & 0 deletions pkg/util/oidcbuilder/oidcbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package oidcbuilder

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"bytes"
"context"
"fmt"
"strings"

azstorage "github.com/Azure/azure-sdk-for-go/storage"
)

type OIDCBuilder struct {
privateKey []byte
publicKey []byte
endpointUrl string
}

func NewOIDCBuilder() *OIDCBuilder {
return &OIDCBuilder{}
}

func (b *OIDCBuilder) EnsureOIDCDocs(ctx context.Context, oidcContainerName string, blobService *azstorage.BlobStorageClient) error {
privateKey, publicKey, err := CreateKeyPair()
b.privateKey = privateKey
b.publicKey = publicKey
if err != nil {
return err
}

// Create the OIDC configuration
discoveryDocument := GenerateDiscoveryDocument(b.endpointUrl)

// Create the OIDC key list
jwks, err := BuildJSONWebKeySet(b.publicKey)
if err != nil {
return err
}

return populateOidcFolder(oidcContainerName, discoveryDocument, jwks, blobService)
}

func (b *OIDCBuilder) SetOIDCEndpointUrl(storageEndpoint string, containerName string) {
b.endpointUrl = fmt.Sprintf("https://%s/%s", storageEndpoint, containerName)
}

func (b *OIDCBuilder) GetEndpointUrl() string {
return b.endpointUrl
}

func (b *OIDCBuilder) GetPrivateKey() string {
return b.endpointUrl
}

func populateOidcFolder(containerName, body string, jwks []byte, blobService *azstorage.BlobStorageClient) error {
bodyKey := ".well-known/openid-configuration"
jwksKey := "openid/v1/jwks"

err := blobService.GetContainerReference(containerName).GetBlobReference(bodyKey).CreateBlockBlobFromReader(strings.NewReader(body), nil)
if err != nil {
return err
}

err = blobService.GetContainerReference(containerName).GetBlobReference(jwksKey).CreateBlockBlobFromReader(bytes.NewReader(jwks), nil)
if err != nil {
return err
}

return nil
}

0 comments on commit dafefa0

Please sign in to comment.