func (s *RSATokenService) healthCheck() health.SubsystemStatus {
	status := health.SubsystemStatus{
		OK:   false,
		Name: "TKN_JWT-RSA",
	}
	if s.privateKey == nil {
		status.Status = "No private key is loaded"
		return status
	}
	if s.publicKey == nil {
		status.Status = "No public key is loaded"
		return status
	}
	if !s.checkKeyModeOK("-rw-r--r--", s.publicKeyFile) {
		status.Status = "Public key has incorrect mode"
		return status
	}
	if !s.checkKeyModeOK("-r--------", s.privateKeyFile) {
		status.Status = "Private key has incorrect mode"
		return status
	}
	status.OK = true
	status.Status = "JWT-RSA TokenService is ready to issue/verify tokens"
	return status
}
healthCheck provides a sanity check that keys are loaded and owned
correctly.
package jwt
import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"path/filepath"
	"github.com/hashicorp/go-hclog"
	"github.com/spf13/viper"
	"github.com/netauth/netauth/internal/health"
	"github.com/netauth/netauth/internal/token"
	"github.com/golang-jwt/jwt/v4"
)
type RSAToken struct {
	token.Claims
	jwt.StandardClaims
}
type RSATokenService struct {
	privateKey *rsa.PrivateKey
	publicKey  *rsa.PublicKey
	publicKeyFile  string
	privateKeyFile string
	log hclog.Logger
}
func init() {
	token.Register("jwt-rsa", NewRSA)
}
func NewRSA(l hclog.Logger) (token.Service, error) {
	x := RSATokenService{}
	x.log = l.Named("jwt-rsa")
	if err := x.GetKeys(); err != nil {
		return nil, err
	}
	health.RegisterCheck("JWT-RSA", x.healthCheck)
	return &x, nil
}
func (s *RSATokenService) Generate(claims token.Claims, config token.Config) (string, error) {
	if s.privateKey == nil {
		return "", token.ErrKeyUnavailable
	}
	c := RSAToken{
		claims,
		jwt.StandardClaims{
			IssuedAt:  config.IssuedAt.Unix(),
			NotBefore: config.NotBefore.Unix(),
			ExpiresAt: config.NotBefore.Add(config.Lifetime).Unix(),
			Subject:   "NetAuth Standard Token",
			Audience:  "Unrestricted",
			Issuer:    config.Issuer,
			Id:        claims.EntityID,
		},
	}
	tkn := jwt.NewWithClaims(jwt.SigningMethodRS512, c)
	ss, _ := tkn.SignedString(s.privateKey)
	return ss, nil
}
func (s *RSATokenService) Validate(tkn string) (token.Claims, error) {
	if s.publicKey == nil {
		return token.Claims{}, token.ErrKeyUnavailable
	}
	t, err := jwt.ParseWithClaims(tkn, &RSAToken{}, func(t *jwt.Token) (interface{}, error) {
		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
			s.log.Error("Token was signed with invalid algorithm", "expected", t.Method, "actual", t.Header["alg"])
			return nil, token.ErrTokenInvalid
		}
		return s.publicKey, nil
	})
	if err != nil {
		if t != nil && !t.Valid {
			return token.Claims{}, token.ErrTokenInvalid
		}
		return token.Claims{}, token.ErrInternalError
	}
	claims, _ := t.Claims.(*RSAToken)
	return claims.Claims, nil
}
func (s *RSATokenService) GetKeys() error {
	s.publicKeyFile = filepath.Join(viper.GetString("core.conf"), "keys", "token.pem")
	s.privateKeyFile = filepath.Join(viper.GetString("core.conf"), "keys", "token.key")
	s.log.Debug("Loading public key", "file", s.publicKeyFile)
	f, err := ioutil.ReadFile(s.publicKeyFile)
	if os.IsNotExist(err) {
		s.log.Error("File contains no key!", "file", s.publicKeyFile)
		if !viper.GetBool("token.jwt.generate") {
			s.log.Warn("Key generation is disabled")
			return token.ErrKeyGenerationDisabled
		}
		s.log.Info("Generating keys")
		if err := s.generateKeys(viper.GetInt("token.jwt.bits")); err != nil {
			s.log.Error("Error generating keys", "error", err)
			return err
		}
		return nil
	}
	if err != nil && !os.IsNotExist(err) {
		s.log.Warn("Keys are not available")
		return token.ErrKeyUnavailable
	}
	if !s.checkKeyModeOK("-rw-r--r--", s.publicKeyFile) {
		s.log.Warn("Public key has incorrect mode bits")
		return token.ErrKeyUnavailable
	}
	block, _ := pem.Decode([]byte(f))
	if block == nil {
		s.log.Error("Error decoding PEM block")
		return token.ErrKeyUnavailable
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		s.log.Error("Error parsing key:", "key", s.publicKeyFile, "error", err)
		return token.ErrKeyUnavailable
	}
	p, ok := pub.(*rsa.PublicKey)
	if !ok {
		s.log.Error("File does not contain an RSA public key", "file", s.publicKeyFile)
		return token.ErrKeyUnavailable
	}
	s.publicKey = p
	s.log.Debug("Loading private key from file", "file", s.privateKeyFile)
	pristr, err := ioutil.ReadFile(s.privateKeyFile)
	if err != nil && !os.IsNotExist(err) {
		s.log.Error("Private key load error", "file", s.privateKeyFile, "error", err)
	}
	if os.IsNotExist(err) {
		s.log.Warn("No private key is loaded")
		s.log.Warn("Signing will be unavailable")
		return nil
	}
	if !s.checkKeyModeOK("-r--------", s.privateKeyFile) {
		s.log.Warn("Private key has incorrect mode bits", "file", s.privateKeyFile)
	}
	block, _ = pem.Decode([]byte(pristr))
	if block == nil {
		s.privateKey = nil
		s.log.Warn("Error decoding private key", "file", s.privateKeyFile)
		return nil
	}
	s.privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		s.privateKey = nil
		s.log.Warn("Error unmarshaling private key", "file", s.privateKeyFile, "error", err)
	}
	return nil
}
func (s *RSATokenService) generateKeys(bits int) error {
	s.log.Debug("Generating keys")
	path := filepath.Join(viper.GetString("core.conf"), "keys")
	if err := os.MkdirAll(path, 0755); err != nil {
		s.log.Error("Could not create key directory", "path", path)
		return token.ErrInternalError
	}
	var err error
	s.privateKey, err = rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		s.log.Error("Error generating keys", "error", err)
		return token.ErrInternalError
	}
	s.publicKey = &s.privateKey.PublicKey
	if err := marshalPrivateKey(s.privateKey, s.privateKeyFile); err != nil {
		return err
	}
	if err := marshalPublicKey(s.publicKey, s.publicKeyFile); err != nil {
		return err
	}
	s.log.Debug("Finished generating keys")
	return nil
}