server added
This commit is contained in:
56
pkg/auxtool/aux509/genpair.go
Normal file
56
pkg/auxtool/aux509/genpair.go
Normal 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
|
||||
}
|
||||
64
pkg/auxtool/aux509/gentls.go
Normal file
64
pkg/auxtool/aux509/gentls.go
Normal 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
|
||||
}
|
||||
28
pkg/auxtool/auxhttp/genres.go
Normal file
28
pkg/auxtool/auxhttp/genres.go
Normal 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)
|
||||
}
|
||||
57
pkg/auxtool/auxhttp/getbearer.go
Normal file
57
pkg/auxtool/auxhttp/getbearer.go
Normal 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
|
||||
}
|
||||
47
pkg/auxtool/auxhttp/parseauth.go
Normal file
47
pkg/auxtool/auxhttp/parseauth.go
Normal 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
|
||||
}
|
||||
92
pkg/auxtool/auxjwt/auxjwt.go
Normal file
92
pkg/auxtool/auxjwt/auxjwt.go
Normal 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
|
||||
}
|
||||
100
pkg/auxtool/auxjwt/auxjwt_test.go
Normal file
100
pkg/auxtool/auxjwt/auxjwt_test.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user