Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagetext
titlePEM to JWKS
package main

import (
        "crypto/rsa"
        "crypto/sha1"
        "crypto/x509"
        "encoding/base64"
        "encoding/json"
        "encoding/pem"
        "flag"
        "fmt"
        "golang.org/x/crypto/ssh"
        "io/ioutil"
        "math/big"
)

type Jwks struct {
        Keys []Key `json:"keys"`
}
type Key struct {
        Kid string `json:"kid,omitempty"`
        Kty string `json:"kty"`
        Use string `json:"use"`
        N   string `json:"n"`
        E   string `json:"e"`
        X5c []string `json:"x5c"`
        X5t string `json:"x5t"`
}

var keyFile string
var keyType string
var certFile string

func getKeyFromPrivate(key []byte) (*rsa.PublicKey){
        parsed, err := ssh.ParseRawPrivateKey(key)
        if err != nil {
                fmt.Println(err)
        }

        // Convert back to an *rsa.PrivateKey
        privateKey := parsed.(*rsa.PrivateKey)

        publicKey := &privateKey.PublicKey
        return publicKey
}

func getKeyFromPublic(key []byte) (*rsa.PublicKey){
        pubPem, _ := pem.Decode(key)

        parsed, err := x509.ParsePKIXPublicKey(pubPem.Bytes)
        if err != nil {
                fmt.Println("Unable to parse RSA public key", err)
        }

        // Convert back to an *rsa.PublicKey
        publicKey := parsed.(*rsa.PublicKey)

        return publicKey
}

func maingetCert(cert []byte) *x509.Certificate {
        flag.StringVar(&keyFile, "keyFile", "/mnt/c/Users/ktimoney/keycloak-certs/client_pub.key", "Location of key file"certPem, _ := pem.Decode(cert)
        flag.StringVar(&keyType, "keyType", "public", "Type of key file")
if certPem == nil {
          flag.Parse()

      panic("Failed to key,parse err := ioutil.ReadFile(keyFilepem file")
        if}

 err !=       // pass cert bytes
        certificate, err := x509.ParseCertificate(certPem.Bytes)
        if err != nil {
                fmt.Println("Unable to parse Certificate", err)
        }

        return certificate
}

func main() {
        flag.StringVar(&keyFile, "keyFile", "/mnt/c/Users/ktimoney/keycloak-certs/client_pub.key", "Location of key file")
        flag.StringVar(&keyType, "keyType", "public", "Type of key file")
        flag.StringVar(&certFile, "certFile", "/mnt/c/Users/ktimoney/keycloak-certs/client.crt", "Location of cert file")
        flag.Parse()

        key, err := ioutil.ReadFile(keyFile)
        if err != nil {
                fmt.Println(err)
        }

        var publicKey *rsa.PublicKey

        if keyType == "public" {
           publicKey = getKeyFromPublic(key)
        }else{
           publicKey = getKeyFromPrivate(key)
        }

        cert, err := ioutil.ReadFile(certFile)
        if err != nil {
                fmt.Println(err)
        }

        varcertificate publicKey *rsa.PublicKey
:= getCert(cert)
        // generate fingerprint with sha1
        if// keyTypeyou == "public" {
   can also use md5, sha256, etc.
        publicKeyfingerprint := getKeyFromPublic(keysha1.Sum(certificate.Raw)

         }elsejwksKey := Key{
           publicKey = getKeyFromPrivate(key)     Kid: "SIGNING_KEY",
        }

        Kty: "RSA",
               jwksKey Use:= Key{ "sig",
                "something" N: base64.RawStdEncoding.EncodeToString(publicKey.N.Bytes()),
                "RSA"E: base64.RawStdEncoding.EncodeToString(big.NewInt(int64(publicKey.E)).Bytes()),
                "sig",
                X5c: []string{base64.RawStdEncoding.EncodeToString(publicKey.N.Bytes())certificate.Raw)},
                X5t: base64.RawStdEncoding.EncodeToString(big.NewInt(int64(publicKey.E)).Bytes()fingerprint[:]),
        }
        jwksKeys := []Key{jwksKey}
        jwks := Jwks{jwksKeys}

        jwksJson, err := json.Marshal(jwks)
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Println(string(jwksJson))

}

...

The output will look like this:

{"keys":[{"kid":"somethingSIGNING_KEY","kty":"RSA","use":"sig","n":"70XsremwRthZafooWzoDbpj1VnebGnHHLBOTT+A87SWv+WLYtnsDRAew/2XlHA8Q2c1sRU5xeEOtV6tNzk7qk8b4Kp6ZjDSeyUu7IjDEKhJUVaW2rJKj6Cyp9V8Iu82owvMdkyipWp2ZRs7GPtT0CDWSjQnu3nhQ2k0KKybZL9OQ7evpEid7q/pzFPZVU3lEUXiNJZAXTx4AIBZTgM2x74NgORg3WDCpxIPoHB1E2lOkOviAhTC6UmOpUBuLhQIrYTrVfpq0FtlgwCho0hf6uMN2fv60d0EOCEvb1a/8uBLZ2CX5oPvMZ1UfJiLwp/FaYOATmmdRF5CQoLO5AvV7CnOWdisISHIDwq+lMmO9qa8TFbgLZ0OcElgws/O1qJSHXISvWYiruT36XRuxRGg17ctTiI1Fb8qtyz1B7Rmpn0dG52aYfwJ+fZbhkKXwo4Q9SAUsAO8aPEotpCmHXPDeLbXVhgxzevr0WOE2bInU+yQYaF43e4AmzuCRPI2EruM4mAogxj8GkG/SCAVT7lWQeuIFyx3BD5wkOgPrniNVsnMeFq7VvXGpIEcbBz4KfVvlMOiNFh8XCP1YIKPtFqdEhx/NerNLVw0EMWx4iyFlTOr+TSVIi0NDr1GCMwu8SSz5gXHWnWTCZAo7lEFlZevYRV/YphJT310Q78CVyh/uPgE","e":"AQAB","x5c":["MIIGSjCCBDKgAwIBAgIUaSy+y5O7hsXcOj9lQB/nYygWXGMwDQYJKoZIhvcNAQELBQAwSjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8GA1UEAwwIZXN0LnRlY2gxGjAYBgkqhkiG9w0BCQEWC2NhQG1haWwuY29tMB4XDTIyMDMyOTEyMjMyOFoXDTIzMDMyOTEyMjMyOFowTjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8GA1UEAwwIZXN0LnRlY2gxHjAcBgkqhkiG9w0BCQEWD2NsaWVudEBtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO9F7K3psEbYWWn6KFs6A26Y9VZ3mxpxxywTk0/gPO0lr/li2LZ7A0QHsP9l5RwPENnNbEVOcXhDrVerTc5O6pPG+CqemYw0nslLuyIwxCoSVFWltqySo+gsqfVfCLvNqMLzHZMoqVqdmUbOxj7U9Ag1ko0J7t54UNpNCism2S/TkO3r6RIne6v6cxT2VVN5RFF4jSWQF08eACAWU4DNse+DYDkYN1gwqcSD6BwdRNpTpDr4gIUwulJjqVAbi4UCK2E61X6atBbZYMAoaNIX+rjDdn7+tHdBDghL29Wv/LgS2dgl+aD7zGdVHyYi8KfxWmDgE5pnUReQkKCzuQL1ewpzlnYrCEhyA8KvpTJjvamvExW4C2dDnBJYMLPztaiUh1yEr1mIq7k9+l0bsURoNe3LU4iNRW/Krcs9Qe0ZqZ9HRudmmH8Cfn2W4ZCl8KOEPUgFLADvGjxKLaQph1zw3i211YYMc3r69FjhNmyJ1PskGGheN3uAJs7gkTyNhK7jOJgKIMY/BpBv0ggFU+5VkHriBcsdwQ+cJDoD654jVbJzHhau1b1xqSBHGwc+Cn1b5TDojRYfFwj9WCCj7RanRIcfzXqzS1cNBDFseIshZUzq/k0lSItDQ69RgjMLvEks+YFx1p1kwmQKO5RBZWXr2EVf2KYSU99dEO/Alcof7j4BAgMBAAGjggEiMIIBHjAdBgNVHQ4EFgQUeo9WwYOqFPJ1B9cjg0WsxS28hnYwgYUGA1UdIwR+MHyAFHoCuHWgHsN1cS7TRuJgk1YvdeY7oU6kTDBKMQswCQYDVQQGEwJJRTEMMAoGA1UECwwDRVNUMREwDwYDVQQDDAhlc3QudGVjaDEaMBgGCSqGSIb3DQEJARYLY2FAbWFpbC5jb22CFG9pjLDcWd36XS7dJnpjRMErPsglMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgL8ME8GA1UdEQRIMEaCCWxvY2FsaG9zdIcEfwAAAYIIbWluaWt1YmWHBMCoMQKCEGtleWNsb2FrLmRlZmF1bHSCEWtleWNsb2FrLmVzdC50ZWNoMAkGA1UdEgQCMAAwDQYJKoZIhvcNAQELBQADggIBABZhxM9RHGrKRZsuRWVGDfhjboMA4NcuE9E2GMiri1gqwnW+C2HlbN1dkmhnED9LFHM64tk1gPOVmvrE2V7nPp58uVCg0xqsQ/qQNWrDj8Gkv5JRKMCPmW3lCTlE/kOkhC9I4b+0+pD6kyqwCS7ThMff5g0tODbVHVvNhCqsPOMYCKBEdKvJ4jhZatpbO1fLXbhnbhAGnUpva3pl8taN/x+z4oAr+j+PkU3VCQBk08w30QwvFHSqJ9UTT3i4B8amIiBt61ZkwX0Xr8z+w4avqtOzcH61UgGtz2LrRlDlz0D1QacGh2/0kGcWUkBT4AU7Fb2RzCaMFB8eiM5bnXXomMTTMLHQSahKig2H6+c/jSJFVLfL9/5i8q1OoPHWBxU6h+OAFF2qWhCuScIvwv+J/RyOd2JwYeIgU8LJ1cWth2/msCQK2S7UO6myzexlevldmXSjRdvHH/cDHlNnjYWNVF5FsVyTj4tgRYpUVWAPTuRR22LoT5+gAW42DTsOa56IoI6NclDm0F4U29qekl2Ktf9zCYZf+PLi8dgf0hwb0TisDbXr2kwURw0m0YiEFsffmL1mtvsptJOlRhBXEcPHn/jLwUGF0odooeC6YY9H3BNYu8sXn2ykAO4AabjBMcoUUh+FGhWXrMjpnG+PSNacPXW/MDijiM30X42YrqcU9zdk"],"x5t":"IIj3g74+BJHIkUZxKWb4zwZeFuw"}]}


We can paste this into the "JWKS" test box.

...

For this to work with you JWT code you'll need to copy the "kid" value and update you code so this is included in the header: This is necessary when the JWKS has multiple keys.

Code Block
languagetext
titleJWT snippet
        claims["iss"] = "jwtclient3"
        claims["aud"] = "https://192.168.49.2:31561/auth/realms/x509"

        token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
        token.Header["kid"] = "AKAwbsKtqu9OmIwIsPOUf5zTJkIC73hzY9Myv4srjTs"
        tokenString, err := token.SignedString(key)
        if err != nil {
                return "", fmt.Errorf("create: sign token: %w", err)
        }


        return tokenString, nil
}

...