From bfe28042add32b69c9a6687e351e8468b9caac3f Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 02:51:41 +0100 Subject: [PATCH 1/2] adding self certificate generation --- pkg/sslcert/options.go | 21 ++++++ pkg/sslcert/sslcert.go | 151 +++++++++++++++++++++++++++++++++++++++ pkg/sslcert/tlsconfig.go | 19 +++++ simplehttpserver.go | 19 ++++- 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 pkg/sslcert/options.go create mode 100644 pkg/sslcert/sslcert.go create mode 100644 pkg/sslcert/tlsconfig.go diff --git a/pkg/sslcert/options.go b/pkg/sslcert/options.go new file mode 100644 index 0000000..54cbc0d --- /dev/null +++ b/pkg/sslcert/options.go @@ -0,0 +1,21 @@ +package sslcert + +import "time" + +type Options struct { + Host string // Comma-separated hostnames and IPs to generate a certificate for") + Organization string + ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011 + ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for + IsCA bool // whether this cert should be its own Certificate Authority + RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set + EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521 + Ed25519Key bool // Generate an Ed25519 key +} + +var DefaultOptions = Options{ + ValidFor: time.Duration(365 * 24 * time.Hour), + IsCA: false, + RSABits: 2048, + Organization: "Acme Co", +} diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go new file mode 100644 index 0000000..6469b78 --- /dev/null +++ b/pkg/sslcert/sslcert.go @@ -0,0 +1,151 @@ +// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go +package sslcert + +import ( + "bufio" + "bytes" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "math/big" + "net" + "strings" + "time" +) + +func pubKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + case ed25519.PrivateKey: + return k.Public().(ed25519.PublicKey) + default: + return nil + } +} + +func Generate(options Options) (privateKey, publicKey []byte, err error) { + if options.Host == "" { + return nil, nil, errors.New("Empty host value") + } + + var priv interface{} + switch options.EcdsaCurve { + case "": + if options.Ed25519Key { + _, priv, err = ed25519.GenerateKey(rand.Reader) + } else { + priv, err = rsa.GenerateKey(rand.Reader, options.RSABits) + } + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve) + return + } + if err != nil { + err = fmt.Errorf("Failed to generate private key: %v", err) + return + } + + // ECDSA, ED25519 and RSA subject keys should have the DigitalSignature + // KeyUsage bits set in the x509.Certificate template + keyUsage := x509.KeyUsageDigitalSignature + // Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In + // the context of TLS this KeyUsage is particular to RSA key exchange and + // authentication. + if _, isRSA := priv.(*rsa.PrivateKey); isRSA { + keyUsage |= x509.KeyUsageKeyEncipherment + } + + var notBefore time.Time + if len(options.ValidFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom) + if err != nil { + err = fmt.Errorf("Failed to parse creation date: %v", err) + return + } + } + + notAfter := notBefore.Add(options.ValidFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + err = fmt.Errorf("Failed to generate serial number: %v", err) + return + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{options.Organization}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: keyUsage, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(options.Host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if options.IsCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv) + if err != nil { + err = fmt.Errorf("Failed to create certificate: %v", err) + return + } + + var pubKeyBuf bytes.Buffer + pubKeyBufb := bufio.NewWriter(&pubKeyBuf) + err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to cert.pem: %v", err) + return + } + + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + err = fmt.Errorf("Unable to marshal private key: %v", err) + return + } + var privKeyBuf bytes.Buffer + privKeyBufb := bufio.NewWriter(&privKeyBuf) + err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to key.pem: %v", err) + return + } + + return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil +} diff --git a/pkg/sslcert/tlsconfig.go b/pkg/sslcert/tlsconfig.go new file mode 100644 index 0000000..e2bbe92 --- /dev/null +++ b/pkg/sslcert/tlsconfig.go @@ -0,0 +1,19 @@ +package sslcert + +import ( + "crypto/tls" +) + +func NewTLSConfig(options Options) (*tls.Config, error) { + pubKey, privKey, err := Generate(options) + if err != nil { + return nil, err + } + + cert, err := tls.X509KeyPair(pubKey, privKey) + if err != nil { + return nil, err + } + + return &tls.Config{Certificates: []tls.Certificate{cert}}, nil +} diff --git a/simplehttpserver.go b/simplehttpserver.go index 98475c6..803d02c 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -11,6 +11,7 @@ import ( "path" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" ) type options struct { @@ -21,6 +22,7 @@ type options struct { Realm string Certificate string Key string + Domain string HTTPS bool Verbose bool Upload bool @@ -35,6 +37,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") + flag.StringVar(&opts.Domain, "domain", "", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.Username, "username", "", "Basic auth username") flag.StringVar(&opts.Password, "password", "", "Basic auth password") @@ -57,9 +60,21 @@ func main() { } if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { - gologger.Fatal().Msgf("Certificate or Key file not specified") + tlsOptions := sslcert.DefaultOptions + tlsOptions.Host = opts.Domain + tlsConfig, err := sslcert.NewTLSConfig(tlsOptions) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + httpServer := &http.Server{ + Addr: opts.ListenAddress, + TLSConfig: tlsConfig, + } + httpServer.Handler = layers + gologger.Print().Msgf("%s\n", httpServer.ListenAndServeTLS("", "")) + } else { + gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } - gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } else { gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers)) } From bd654bf7de05652c7cc25912482156fb607db277 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 17:58:09 +0100 Subject: [PATCH 2/2] adding missing buffer flush --- pkg/sslcert/sslcert.go | 2 ++ simplehttpserver.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go index 6469b78..862ee21 100644 --- a/pkg/sslcert/sslcert.go +++ b/pkg/sslcert/sslcert.go @@ -133,6 +133,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to cert.pem: %v", err) return } + pubKeyBufb.Flush() privBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { @@ -146,6 +147,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to key.pem: %v", err) return } + privKeyBufb.Flush() return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil } diff --git a/simplehttpserver.go b/simplehttpserver.go index 803d02c..f8e5875 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -37,7 +37,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") - flag.StringVar(&opts.Domain, "domain", "", "Domain") + flag.StringVar(&opts.Domain, "domain", "local.host", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.Username, "username", "", "Basic auth username") flag.StringVar(&opts.Password, "password", "", "Basic auth password")