/
crypt.go
107 lines (94 loc) · 2.86 KB
/
crypt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package auth
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"math/big"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/lestrrat-go/jwx/jwk"
"golang.org/x/crypto/bcrypt"
)
// HashPassword creates a cryptograhic hash of a password
func HashPassword(password string) (string, error) {
// bcrypt.MaxCost takes very very very long
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost+4)
return string(bytes), err
}
// MustHashPassword creates a cryptographic hash of a password or panics
func MustHashPassword(pw string) string {
hashed, err := HashPassword(pw)
if err != nil {
panic(err)
}
return hashed
}
// CheckPasswordHash compares a password against its hash
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// JWK encodes a JSON web key
type JWK struct {
KID string `json:"kid"`
Algorithm string `json:"alg"`
E string `json:"e"`
KTY string `json:"kty"`
N string `json:"n"`
}
// ToJwks converts a RSA public key to a JWK set
func ToJwks(pub *rsa.PublicKey) (jwk.Set, error) {
jwkJSON, err := ToJwksJSON(pub)
if err != nil {
return nil, err
}
jwkSet, err := jwk.Parse(jwkJSON)
if err != nil {
return nil, err
}
return jwkSet, nil
}
// ToJwksJSON converts a RSA public key to a JSON encoded JWK set
func ToJwksJSON(pub *rsa.PublicKey) ([]byte, error) {
// See https://github.com/golang/crypto/blob/master/acme/jws.go#L90
// https://tools.ietf.org/html/rfc7518#section-6.3.1
n := pub.N
e := big.NewInt(int64(pub.E))
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return json.Marshal(JWK{
KID: "0",
Algorithm: "RS256",
E: base64.RawURLEncoding.EncodeToString(e.Bytes()),
KTY: "RSA",
N: base64.RawURLEncoding.EncodeToString(n.Bytes()),
})
}
// ToPEM converts a RSA private key into PEM format
func ToPEM(key *rsa.PrivateKey) []byte {
return pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
},
)
}
// SignJwtClaims signs JWT claims using RS256 and returns the token string
func (auth *Authenticator) SignJwtClaims(claims Claims) (string, error) {
expirationTime := time.Now().Add(auth.ExpiresAfter)
// set structured JWT claims set
// https://pkg.go.dev/github.com/golang-jwt/jwt/v4#RegisteredClaims
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
reg := claims.GetRegisteredClaims()
reg.ExpiresAt = jwt.NewNumericDate(expirationTime)
reg.Issuer = auth.Issuer
reg.Audience = jwt.ClaimStrings([]string{auth.Audience})
// create the token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = "0"
token.Header["alg"] = "RS256"
// sign the token
return token.SignedString(auth.SignKey)
}