server added

This commit is contained in:
2023-07-31 18:30:43 +02:00
parent dbbd2884ea
commit 2d7db4467a
66 changed files with 13251 additions and 105 deletions

View File

@@ -0,0 +1,56 @@
package aux509
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func GenerateCAPair(orgName, commonName string) (string, string, error) {
var err error
var crtPem string
var keyPem string
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %w", err)
return crtPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = string(pem.EncodeToMemory(&keyPemBlock))
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{orgName},
},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return crtPem, keyPem, fmt.Errorf("Can't create a certificate: %w", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem := string(pem.EncodeToMemory(&certPemBlock))
return certPem, keyPem, err
}

View File

@@ -0,0 +1,64 @@
package aux509
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func GetTLSCert(orgName, commonName string) (tls.Certificate, error) {
var err error
var res tls.Certificate
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %w", err)
return res, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem := string(pem.EncodeToMemory(&keyPemBlock))
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{orgName},
},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return res, fmt.Errorf("Can't create a certificate: %w", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem := string(pem.EncodeToMemory(&certPemBlock))
if err != nil {
return res, err
}
tlsCert, err := tls.X509KeyPair([]byte(certPem), []byte(keyPem))
if err != nil {
return res, err
}
res = tlsCert
return res, err
}

View File

@@ -0,0 +1,28 @@
package auxhttp
import (
"net/http"
"github.com/gin-gonic/gin"
)
type GenericResponse[T any] struct {
Result T `json:"result,omitempty"`
Message string `json:"message,omitempty"`
Error bool `json:"error"`
}
func SendError(c *gin.Context, err error) {
var response GenericResponse[interface{}]
response.Error = true
if err != nil {
response.Message = err.Error()
}
c.AbortWithStatusJSON(http.StatusOK, response)
}
func SendResult(c *gin.Context, result any) {
var response GenericResponse[any]
response.Result = result
c.JSON(http.StatusOK, response)
}

View File

@@ -0,0 +1,57 @@
package auxhttp
import (
"errors"
"fmt"
"strings"
)
func GetBearerToken(authHeader string) (string, error) {
var err error
var res string
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
err = errors.New("Authorization key and value not found")
return res, err
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
err = fmt.Errorf("Authorization type is different from %s", bearerKey)
return res, err
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return res, errors.New("Lenght of authorization token must be greater zero")
}
res = token
return res, err
}
func HaveBearerToken(authHeader string) bool {
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
return false
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
return false
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return false
}
return true
}

View File

@@ -0,0 +1,47 @@
package auxhttp
import (
"encoding/base64"
"errors"
"strings"
)
func ParseAuthBasicHeader(header string) (string, string, error) {
var err error
var username string
var password string
authData := strings.SplitN(header, " ", 2)
if len(authData) < 2 {
err = errors.New("Wrong authentification header")
return username, password, err
}
authType := strings.TrimSpace(authData[0])
if authType != "Basic" {
err = errors.New("Authentification type is different from basic")
return username, password, err
}
authPair := strings.TrimSpace(authData[1])
pairEncoded, err := base64.StdEncoding.DecodeString(authPair)
if err != nil {
return username, password, err
}
pair := strings.SplitN(string(pairEncoded), ":", 2)
if len(pair) < 2 {
err = errors.New("Wrong authentification pair")
return username, password, err
}
username = strings.TrimSpace(pair[0])
password = strings.TrimSpace(pair[1])
if username == "" {
err = errors.New("autentification username is null")
return username, password, err
}
if password == "" {
err = errors.New("autentification password is null")
return username, password, err
}
return username, password, err
}

View File

@@ -0,0 +1,92 @@
package auxjwt
import (
"errors"
"math"
"time"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwt"
)
const (
SessionIDKey = "sessionId"
jwtAlg = jwa.HS256
)
func MakeJWTToken(issuer, subject string, sessionID int64, lifetime time.Duration, secret []byte) ([]byte, int64, error) {
var err error
issueTime := time.Now()
expireTime := time.Now().Add(lifetime)
expireUnix := expireTime.Unix()
token := jwt.New()
token.Set(jwt.IssuerKey, issuer)
token.Set(jwt.SubjectKey, subject)
token.Set(jwt.ExpirationKey, expireTime)
token.Set(jwt.IssuedAtKey, issueTime)
token.Set(jwt.IssuedAtKey, issueTime)
token.Set(SessionIDKey, sessionID)
tokenBytes, err := jwt.Sign(token, jwtAlg, []byte(secret))
if err != nil {
return tokenBytes, expireUnix, err
}
return tokenBytes, expireUnix, err
}
func GetJWTSessionID(tokenBytes, secret []byte) (int64, error) {
var err error
var sessionID int64
parseOpt := []jwt.ParseOption{
jwt.WithVerify(jwtAlg, secret),
jwt.WithValidator(jwt.IsExpirationValid()),
jwt.WithValidate(true),
}
token, err := jwt.Parse(tokenBytes, parseOpt...)
if err != nil {
return sessionID, err
}
anyValue, exists := token.Get(SessionIDKey)
if !exists {
err := errors.New("Token does not include value is sessionId")
return sessionID, err
}
fValue, convOk := anyValue.(float64)
if !convOk {
err := errors.New("Cannont convert value of sessionId to float64")
return sessionID, err
}
sessionID = int64(math.Round(fValue))
return sessionID, err
}
func ValidateJWTToken(tokenBytes []byte, issuer, subject string, secret []byte) error {
parseOpt := []jwt.ParseOption{
jwt.WithVerify(jwtAlg, secret),
jwt.WithValidator(jwt.IsExpirationValid()),
jwt.WithValidate(true),
}
token, err := jwt.Parse(tokenBytes, parseOpt...)
if err != nil {
return err
}
validateOpt := []jwt.ValidateOption{
jwt.WithSubject(subject),
jwt.WithIssuer(issuer),
jwt.WithValidator(jwt.IsIssuedAtValid()),
jwt.WithValidator(jwt.IsExpirationValid()),
}
err = jwt.Validate(token, validateOpt...)
if err != nil {
return err
}
return err
}

View File

@@ -0,0 +1,100 @@
package auxjwt
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestJWTGood(t *testing.T) {
const issuer = "Issuer"
const subject = "Subject"
const lifetime = 10 * time.Second
secret := []byte("12345")
var sessionId int64 = 123456789
var err error
tokenBytes, _, err := MakeJWTToken(issuer, subject, sessionId, lifetime, secret)
require.NoError(t, err)
require.NotNil(t, tokenBytes)
recvSessionID, err := GetJWTSessionID(tokenBytes, secret)
require.NoError(t, err)
require.Equal(t, sessionId, recvSessionID)
err = ValidateJWTToken(tokenBytes, issuer, subject, secret)
require.NoError(t, err)
}
func TestJWTTimeExpired(t *testing.T) {
const issuer = "Issuer"
const subject = "Subject"
const lifetime = 1 * time.Second
secret := []byte("12345")
var sessionId int64 = 123456789
var err error
tokenBytes, _, err := MakeJWTToken(issuer, subject, sessionId, lifetime, secret)
require.NoError(t, err)
require.NotNil(t, tokenBytes)
time.Sleep(2 * time.Second)
err = ValidateJWTToken(tokenBytes, issuer, subject, secret)
require.Error(t, err)
}
func TestJWTWrongSecret(t *testing.T) {
const issuer = "Issuer"
const subject = "Subject"
const lifetime = 10 * time.Second
secret := []byte("12345")
var sessionId int64 = 123456789
var err error
tokenBytes, _, err := MakeJWTToken(issuer, subject, sessionId, lifetime, secret)
require.NoError(t, err)
require.NotNil(t, tokenBytes)
secret = []byte("12345+1")
err = ValidateJWTToken(tokenBytes, issuer, subject, secret)
require.Error(t, err)
}
func TestJWTWrongIssuer(t *testing.T) {
var issuer = "Issuer"
const subject = "Subject"
const lifetime = 10 * time.Second
secret := []byte("12345")
var sessionId int64 = 123456789
var err error
tokenBytes, _, err := MakeJWTToken(issuer, subject, sessionId, lifetime, secret)
require.NoError(t, err)
require.NotNil(t, tokenBytes)
issuer = "mr.santa"
err = ValidateJWTToken(tokenBytes, issuer, subject, secret)
require.Error(t, err)
}
func TestJWTWrongSubject(t *testing.T) {
const issuer = "Issuer"
var subject = "Subject"
const lifetime = 10 * time.Second
secret := []byte("12345")
var sessionId int64 = 123456789
var err error
tokenBytes, _, err := MakeJWTToken(issuer, subject, sessionId, lifetime, secret)
require.NoError(t, err)
require.NotNil(t, tokenBytes)
subject = "$$$"
err = ValidateJWTToken(tokenBytes, issuer, subject, secret)
require.Error(t, err)
}