|
@@ -0,0 +1,129 @@
|
|
|
|
+package tlscert
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "crypto/tls"
|
|
|
|
+ "crypto/x509"
|
|
|
|
+ "encoding/pem"
|
|
|
|
+ "io"
|
|
|
|
+ "io/ioutil"
|
|
|
|
+ "log"
|
|
|
|
+ "os"
|
|
|
|
+ "path/filepath"
|
|
|
|
+ "strings"
|
|
|
|
+
|
|
|
|
+ "imuslab.com/arozos/ReverseProxy/mod/utils"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+type Manager struct {
|
|
|
|
+ CertStore string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func NewManager(certStore string) (*Manager, error) {
|
|
|
|
+ if !utils.FileExists(certStore) {
|
|
|
|
+ os.MkdirAll(certStore, 0775)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ thisManager := Manager{
|
|
|
|
+ CertStore: certStore,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &thisManager, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (m *Manager) ListCertDomains() ([]string, error) {
|
|
|
|
+ filenames, err := m.ListCerts()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return []string{}, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //Remove certificates where there are missing public key or private key
|
|
|
|
+ filenames = getCertPairs(filenames)
|
|
|
|
+
|
|
|
|
+ return filenames, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (m *Manager) ListCerts() ([]string, error) {
|
|
|
|
+ certs, err := ioutil.ReadDir(m.CertStore)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return []string{}, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ filenames := make([]string, 0, len(certs))
|
|
|
|
+ for _, cert := range certs {
|
|
|
|
+ if !cert.IsDir() {
|
|
|
|
+ filenames = append(filenames, cert.Name())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return filenames, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (m *Manager) GetCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
|
|
+ //Check if the domain corrisponding cert exists
|
|
|
|
+ pubKey := "./system/localhost.crt"
|
|
|
|
+ priKey := "./system/localhost.key"
|
|
|
|
+
|
|
|
|
+ if utils.FileExists(filepath.Join(m.CertStore, helloInfo.ServerName+".crt")) && utils.FileExists(filepath.Join(m.CertStore, helloInfo.ServerName+".key")) {
|
|
|
|
+ pubKey = filepath.Join(m.CertStore, helloInfo.ServerName+".crt")
|
|
|
|
+ priKey = filepath.Join(m.CertStore, helloInfo.ServerName+".key")
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ domainCerts, _ := m.ListCertDomains()
|
|
|
|
+ cloestDomainCert := matchClosestDomainCertificate(helloInfo.ServerName, domainCerts)
|
|
|
|
+ if cloestDomainCert != "" {
|
|
|
|
+ //There is a matching parent domain for this subdomain. Use this instead.
|
|
|
|
+ pubKey = filepath.Join(m.CertStore, cloestDomainCert+".crt")
|
|
|
|
+ priKey = filepath.Join(m.CertStore, cloestDomainCert+".key")
|
|
|
|
+ } else {
|
|
|
|
+ log.Println("Matching certificate not found. Serving with default. Domain: ", helloInfo.ServerName)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ cer, err := tls.LoadX509KeyPair(pubKey, priKey)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Println(err)
|
|
|
|
+ return nil, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &cer, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//Check if the given file is a valid TLS file
|
|
|
|
+func isValidTLSFile(file io.Reader) bool {
|
|
|
|
+ // Read the contents of the uploaded file
|
|
|
|
+ contents, err := io.ReadAll(file)
|
|
|
|
+ if err != nil {
|
|
|
|
+ // Handle the error
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Parse the contents of the file as a PEM-encoded certificate or key
|
|
|
|
+ block, _ := pem.Decode(contents)
|
|
|
|
+ if block == nil {
|
|
|
|
+ // The file is not a valid PEM-encoded certificate or key
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Parse the certificate or key
|
|
|
|
+ if strings.Contains(block.Type, "CERTIFICATE") {
|
|
|
|
+ // The file contains a certificate
|
|
|
|
+ cert, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
+ if err != nil {
|
|
|
|
+ // Handle the error
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ // Check if the certificate is a valid TLS/SSL certificate
|
|
|
|
+ return cert.IsCA == false && cert.KeyUsage&x509.KeyUsageDigitalSignature != 0 && cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0
|
|
|
|
+ } else if strings.Contains(block.Type, "PRIVATE KEY") {
|
|
|
|
+ // The file contains a private key
|
|
|
|
+ _, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
|
|
+ if err != nil {
|
|
|
|
+ // Handle the error
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ return true
|
|
|
|
+ } else {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|