Commit cf78cdef authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Improve private and public key configuration

parent ae84e177
...@@ -138,22 +138,40 @@ func CopyDirectory(src, dest string) error { ...@@ -138,22 +138,40 @@ func CopyDirectory(src, dest string) error {
) )
} }
func ReadKey(key string) ([]byte, error) { // ReadKey returns either the content of the file specified at path, if it exists,
// or []byte(key) otherwise. It is an error to specify both or none arguments, or
// specify an empty or unreadable file. If there is no error then the return []byte is non-empty.
func ReadKey(key, path string) ([]byte, error) {
if (key != "" && path != "") || (key == "" && path == "") {
return nil, errors.New("provide either key or path to key")
}
var bts []byte var bts []byte
if stat, err := os.Stat(key); err == nil {
if path == "" {
bts = []byte(key)
} else {
stat, err := os.Stat(path)
if err != nil {
return nil, errors.New("no key found at specified path")
}
if stat.IsDir() { if stat.IsDir() {
return nil, errors.New("cannot read key from a directory") return nil, errors.New("cannot read key from a directory")
} }
bts, err = ioutil.ReadFile(key) bts, err = ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { }
bts = []byte(key)
if len(bts) == 0 {
return nil, errors.New("empty key provided")
} }
return bts, nil return bts, nil
} }
// Base64Decode decodes the specified bytes as any of the Base64 dialects:
// standard encoding (+, /) and URL encoding (-, _), with or without padding.
func Base64Decode(b []byte) ([]byte, error) { func Base64Decode(b []byte) ([]byte, error) {
var ( var (
err error err error
......
...@@ -162,8 +162,9 @@ func postRequest(serverurl string, request irma.RequestorRequest, name, authmeth ...@@ -162,8 +162,9 @@ func postRequest(serverurl string, request irma.RequestorRequest, name, authmeth
jwtstr string jwtstr string
bts []byte bts []byte
) )
if bts, err = fs.ReadKey(key); err != nil { // If the key refers to an existing file, use contents of the file as key
return nil, nil, err if bts, err = fs.ReadKey("", key); err != nil {
bts = []byte(key)
} }
if authmethod == "hmac" { if authmethod == "hmac" {
jwtalg = jwt.SigningMethodHS256 jwtalg = jwt.SigningMethodHS256
......
...@@ -77,14 +77,9 @@ func (hauth *HmacAuthenticator) Authenticate( ...@@ -77,14 +77,9 @@ func (hauth *HmacAuthenticator) Authenticate(
} }
func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error { func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error {
if requestor.AuthenticationKey == "" { bts, err := fs.ReadKey(requestor.AuthenticationKey, requestor.AuthenticationKeyFile)
return errors.Errorf("Requestor %s has no authentication key", name)
}
// Read file contents or string contents
bts, err := fs.ReadKey(requestor.AuthenticationKey)
if err != nil { if err != nil {
return err return errors.WrapPrefix(err, "Failed to read key of requestor "+name, 0)
} }
// We accept any of the base64 encodings // We accept any of the base64 encodings
...@@ -105,13 +100,11 @@ func (pkauth *PublicKeyAuthenticator) Authenticate( ...@@ -105,13 +100,11 @@ func (pkauth *PublicKeyAuthenticator) Authenticate(
} }
func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requestor) error { func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requestor) error {
bts, err := fs.ReadKey(requestor.AuthenticationKey) bts, err := fs.ReadKey(requestor.AuthenticationKey, requestor.AuthenticationKeyFile)
if err != nil { if err != nil {
return err return errors.WrapPrefix(err, "Failed to read key of requestor "+name, 0)
}
if len(bts) == 0 {
return errors.Errorf("Requestor %s has invalid public key", name)
} }
pk, err := jwt.ParseRSAPublicKeyFromPEM(bts) pk, err := jwt.ParseRSAPublicKeyFromPEM(bts)
if err != nil { if err != nil {
return err return err
...@@ -140,12 +133,9 @@ func (pskauth *PresharedKeyAuthenticator) Authenticate( ...@@ -140,12 +133,9 @@ func (pskauth *PresharedKeyAuthenticator) Authenticate(
} }
func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error { func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error {
if requestor.AuthenticationKey == "" { bts, err := fs.ReadKey(requestor.AuthenticationKey, requestor.AuthenticationKeyFile)
return errors.Errorf("Requestor %s has no authentication key", name)
}
bts, err := fs.ReadKey(requestor.AuthenticationKey)
if err != nil { if err != nil {
return err return errors.WrapPrefix(err, "Failed to read key of requestor "+name, 0)
} }
pskauth.presharedkeys[string(bts)] = name pskauth.presharedkeys[string(bts)] = name
return nil return nil
......
...@@ -3,7 +3,6 @@ package irmaserver ...@@ -3,7 +3,6 @@ package irmaserver
import ( import (
"crypto/rsa" "crypto/rsa"
"fmt" "fmt"
"io/ioutil"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
...@@ -45,7 +44,8 @@ type Configuration struct { ...@@ -45,7 +44,8 @@ type Configuration struct {
JwtIssuer string `json:"jwtissuer" mapstructure:"jwtissuer"` JwtIssuer string `json:"jwtissuer" mapstructure:"jwtissuer"`
// Private key to sign result JWTs with. If absent, /result-jwt and /getproof are disabled. // Private key to sign result JWTs with. If absent, /result-jwt and /getproof are disabled.
JwtPrivateKey string `json:"jwtprivatekey" mapstructure:"jwtprivatekey"` JwtPrivateKey string `json:"jwtprivatekey" mapstructure:"jwtprivatekey"`
JwtPrivateKeyFile string `json:"jwtprivatekeyfile" mapstructure:"jwtprivatekeyfile"`
// Max age in seconds of a session request JWT (using iat field) // Max age in seconds of a session request JWT (using iat field)
MaxRequestAge int `json:"maxrequestage" mapstructure:"maxrequestage"` MaxRequestAge int `json:"maxrequestage" mapstructure:"maxrequestage"`
...@@ -68,8 +68,9 @@ type Permissions struct { ...@@ -68,8 +68,9 @@ type Permissions struct {
type Requestor struct { type Requestor struct {
Permissions `mapstructure:",squash"` Permissions `mapstructure:",squash"`
AuthenticationMethod AuthenticationMethod `json:"authmethod" mapstructure:"authmethod"` AuthenticationMethod AuthenticationMethod `json:"authmethod" mapstructure:"authmethod"`
AuthenticationKey string `json:"key" mapstructure:"key"` AuthenticationKey string `json:"key" mapstructure:"key"`
AuthenticationKeyFile string `json:"keyfile" mapstructure:"keyfile"`
} }
// CanIssue returns whether or not the specified requestor may issue the specified credentials. // CanIssue returns whether or not the specified requestor may issue the specified credentials.
...@@ -262,21 +263,13 @@ func (conf *Configuration) validatePermissionSet(requestor string, requestorperm ...@@ -262,21 +263,13 @@ func (conf *Configuration) validatePermissionSet(requestor string, requestorperm
} }
func (conf *Configuration) readPrivateKey() error { func (conf *Configuration) readPrivateKey() error {
if conf.JwtPrivateKey == "" { if conf.JwtPrivateKey == "" && conf.JwtPrivateKeyFile == "" {
return nil return nil
} }
var keybytes []byte keybytes, err := fs.ReadKey(conf.JwtPrivateKey, conf.JwtPrivateKeyFile)
var err error if err != nil {
if strings.HasPrefix(conf.JwtPrivateKey, "-----BEGIN") { return errors.WrapPrefix(err, "failed to read private key", 0)
keybytes = []byte(conf.JwtPrivateKey)
} else {
if err = fs.AssertPathExists(conf.JwtPrivateKey); err != nil {
return err
}
if keybytes, err = ioutil.ReadFile(conf.JwtPrivateKey); err != nil {
return err
}
} }
conf.jwtPrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM(keybytes) conf.jwtPrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM(keybytes)
......
...@@ -75,7 +75,8 @@ func setFlags(cmd *cobra.Command) error { ...@@ -75,7 +75,8 @@ func setFlags(cmd *cobra.Command) error {
flags.String("cachepath", cachepath, "Directory for writing cache files to") flags.String("cachepath", cachepath, "Directory for writing cache files to")
flags.Uint("schemeupdate", 60, "Update IRMA schemes every x minutes (0 to disable)") flags.Uint("schemeupdate", 60, "Update IRMA schemes every x minutes (0 to disable)")
flags.StringP("jwtissuer", "j", "irmaserver", "JWT issuer") flags.StringP("jwtissuer", "j", "irmaserver", "JWT issuer")
flags.StringP("jwtprivatekey", "w", "", "JWT private key or path to it") flags.String("jwtprivatekey", "", "JWT private key")
flags.String("jwtprivatekeyfile", "", "Path to JWT private key")
flags.Int("maxrequestage", 300, "Max age in seconds of a session request JWT") flags.Int("maxrequestage", 300, "Max age in seconds of a session request JWT")
flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects") flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects")
flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen") flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen")
...@@ -151,6 +152,7 @@ func configure() error { ...@@ -151,6 +152,7 @@ func configure() error {
GlobalPermissions: irmaserver.Permissions{}, GlobalPermissions: irmaserver.Permissions{},
JwtIssuer: viper.GetString("jwtissuer"), JwtIssuer: viper.GetString("jwtissuer"),
JwtPrivateKey: viper.GetString("jwtprivatekey"), JwtPrivateKey: viper.GetString("jwtprivatekey"),
JwtPrivateKeyFile: viper.GetString("jwtprivatekeyfile"),
MaxRequestAge: viper.GetInt("maxrequestage"), MaxRequestAge: viper.GetInt("maxrequestage"),
Verbose: viper.GetInt("verbose"), Verbose: viper.GetInt("verbose"),
Quiet: viper.GetBool("quiet"), Quiet: viper.GetBool("quiet"),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment