Last active
June 13, 2019 02:45
-
-
Save sam-w/6d00416c65d30c48ff11726d44dc929f to your computer and use it in GitHub Desktop.
Golang RSA Keys and JWTs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ( | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/x509" | |
"encoding/pem" | |
"errors" | |
"fmt" | |
"time" | |
"gopkg.in/square/go-jose.v2" | |
"gopkg.in/square/go-jose.v2/jwt" | |
) | |
func foo() string { | |
privateKey, err := rsa.GenerateKey(rand.Reader, 2048) | |
if err != nil { | |
panic(err) | |
} | |
// Export the keys to pem string | |
privatePem := exportRsaPrivateKeyAsPemStr(privateKey) | |
publicPem, _ := exportRsaPublicKeyAsPemStr(&privateKey.PublicKey) | |
// Import the keys from pem string | |
privateParsed, _ := parseRsaPrivateKeyFromPemStr(privatePem) | |
publicParsed, _ := parseRsaPublicKeyFromPemStr(publicPem) | |
// Export the newly imported keys | |
privateParsedPem := exportRsaPrivateKeyAsPemStr(privateParsed) | |
publicParsedPem, _ := exportRsaPublicKeyAsPemStr(publicParsed) | |
// Check that the exported/imported keys match the original keys | |
if privatePem != privateParsedPem || publicPem != publicParsedPem { | |
panic("Failure: Export and Import did not result in same Keys") | |
} | |
// Instantiate a signer using RSASHA256 with the given private key. | |
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: privateKey}, (&jose.SignerOptions{}).WithType("JWT")) | |
if err != nil { | |
panic(err) | |
} | |
claims := jwt.Claims{ | |
Subject: "abc123", | |
Issuer: "consumer-app-layer", | |
IssuedAt: jwt.NewNumericDate(time.Now()), | |
Expiry: jwt.NewNumericDate(time.Now().Add(36000)), | |
ID: "OUI5MzlEQjBFMTA3OUExMTg4MTkyNUQzMUFCMDYyREM3NkUwNkUyMA", | |
Audience: jwt.Audience{"Old Robby Bobson"}, | |
} | |
privateClaims := struct { | |
Nickname string `json:"nickname"` | |
Name string `json:"name"` | |
ParseSessionToken string `json:"X-Parse-Session-Token"` | |
}{ | |
"Bob", | |
"Robert Bobson", | |
"def456", | |
} | |
// NOW VALIDATE IT | |
token, err := jwt.Signed(signer).Claims(claims).Claims(privateClaims).Token() | |
if err != nil { | |
panic(err) | |
} | |
containedClaims := jwt.Claims{} | |
key := &privateKey.PublicKey | |
if err = token.Claims(key, &containedClaims); err != nil { | |
panic(err) | |
} | |
expected := jwt.Expected{Issuer: "consumer-app-layer", Audience: []string{"Old Robby Bobson"}}.WithTime(time.Now()) | |
err = claims.ValidateWithLeeway(expected, 1*time.Minute) | |
if err != nil { | |
panic(err) | |
} | |
raw, err := jwt.Signed(signer).Claims(claims).Claims(privateClaims).CompactSerialize() | |
if err != nil { | |
panic(err) | |
} | |
return raw | |
} | |
func exportRsaPrivateKeyAsPemStr(p *rsa.PrivateKey) string { | |
bytes := x509.MarshalPKCS1PrivateKey(p) | |
pem := pem.EncodeToMemory( | |
&pem.Block{ | |
Type: "RSA PRIVATE KEY", | |
Bytes: bytes, | |
}, | |
) | |
return string(pem) | |
} | |
func parseRsaPrivateKeyFromPemStr(p string) (*rsa.PrivateKey, error) { | |
block, _ := pem.Decode([]byte(p)) | |
if block == nil { | |
return nil, errors.New("failed to parse PEM block containing the key") | |
} | |
key, err := x509.ParsePKCS1PrivateKey(block.Bytes) | |
if err != nil { | |
return nil, err | |
} | |
return key, nil | |
} | |
func exportRsaPublicKeyAsPemStr(p *rsa.PublicKey) (string, error) { | |
bytes, err := x509.MarshalPKIXPublicKey(p) | |
if err != nil { | |
return "", err | |
} | |
pem := pem.EncodeToMemory( | |
&pem.Block{ | |
Type: "RSA PUBLIC KEY", | |
Bytes: bytes, | |
}, | |
) | |
return string(pem), nil | |
} | |
func parseRsaPublicKeyFromPemStr(p string) (*rsa.PublicKey, error) { | |
block, _ := pem.Decode([]byte(p)) | |
if block == nil { | |
return nil, errors.New("failed to parse PEM block containing the key") | |
} | |
key, err := x509.ParsePKIXPublicKey(block.Bytes) | |
if err != nil { | |
return nil, err | |
} | |
switch key := key.(type) { | |
case *rsa.PublicKey: | |
return key, nil | |
default: | |
break // fall through | |
} | |
return nil, errors.New("Key type is not RSA") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment