diff --git a/modules/api_rest.go b/modules/api_rest.go index 811fa6b4..4572fb23 100644 --- a/modules/api_rest.go +++ b/modules/api_rest.go @@ -63,6 +63,8 @@ func NewRestAPI(s *session.Session) *RestAPI { "", "API TLS certificate.")) + tls.CertConfigToModule("api.rest", &api.SessionModule, tls.DefaultLegitConfig) + api.AddParam(session.NewStringParameter("api.rest.key", "~/.bcap-api.rest.key.pem", "", @@ -132,10 +134,18 @@ func (api *RestAPI) Configure() error { return err } else if err, api.useWebsocket = api.BoolParam("api.rest.websocket"); err != nil { return err - } else if !core.Exists(api.certFile) || !core.Exists(api.keyFile) { + } + + if !core.Exists(api.certFile) || !core.Exists(api.keyFile) { + err, cfg := tls.CertConfigFromModule("api.rest", api.SessionModule) + if err != nil { + return err + } + + log.Debug("%+v", cfg) log.Info("Generating TLS key to %s", api.keyFile) log.Info("Generating TLS certificate to %s", api.certFile) - if err := tls.Generate(api.certFile, api.keyFile); err != nil { + if err := tls.Generate(cfg, api.certFile, api.keyFile); err != nil { return err } } else { diff --git a/modules/http_server.go b/modules/http_server.go index e9177fb0..a06e5865 100644 --- a/modules/http_server.go +++ b/modules/http_server.go @@ -50,6 +50,8 @@ func NewHttpServer(s *session.Session) *HttpServer { "", "TLS key file, if not empty will configure this as a HTTPS server (will be auto generated if filled but not existing).")) + tls.CertConfigToModule("http.server", &httpd.SessionModule, tls.DefaultLegitConfig) + httpd.AddHandler(session.NewModuleHandler("http.server on", "", "Start httpd server.", func(args []string) error { @@ -131,9 +133,15 @@ func (httpd *HttpServer) Configure() error { if certFile != "" && keyFile != "" { if !core.Exists(certFile) || !core.Exists(keyFile) { + err, cfg := tls.CertConfigFromModule("http.server", httpd.SessionModule) + if err != nil { + return err + } + + log.Debug("%+v", cfg) log.Info("Generating server TLS key to %s", keyFile) log.Info("Generating server TLS certificate to %s", certFile) - if err := tls.Generate(certFile, keyFile); err != nil { + if err := tls.Generate(cfg, certFile, keyFile); err != nil { return err } } else { diff --git a/modules/https_proxy.go b/modules/https_proxy.go index 218033e5..29a3da2c 100644 --- a/modules/https_proxy.go +++ b/modules/https_proxy.go @@ -50,6 +50,8 @@ func NewHttpsProxy(s *session.Session) *HttpsProxy { "", "HTTPS proxy certification authority TLS key file.")) + tls.CertConfigToModule("https.proxy", &p.SessionModule, tls.DefaultSpoofConfig) + p.AddParam(session.NewStringParameter("https.proxy.script", "", "", @@ -118,9 +120,15 @@ func (p *HttpsProxy) Configure() error { } if !core.Exists(certFile) || !core.Exists(keyFile) { + err, cfg := tls.CertConfigFromModule("https.proxy", p.SessionModule) + if err != nil { + return err + } + + log.Debug("%+v", cfg) log.Info("Generating proxy certification authority TLS key to %s", keyFile) log.Info("Generating proxy certification authority TLS certificate to %s", certFile) - if err := tls.Generate(certFile, keyFile); err != nil { + if err := tls.Generate(cfg, certFile, keyFile); err != nil { return err } } else { diff --git a/tls/cert.go b/tls/cert.go index b4413588..ad75d814 100644 --- a/tls/cert.go +++ b/tls/cert.go @@ -8,25 +8,76 @@ import ( "encoding/pem" "math/big" "os" + "strconv" "time" + + "github.com/bettercap/bettercap/session" ) -func Generate(certPath string, keyPath string) error { - keyfile, err := os.Create(keyPath) - if err != nil { - return err - } - defer keyfile.Close() +type CertConfig struct { + Bits int + Country string + Locality string + Organization string + OrganizationalUnit string + CommonName string +} - certfile, err := os.Create(certPath) - if err != nil { - return err +var ( + DefaultLegitConfig = CertConfig{ + Bits: 4096, + Country: "US", + Locality: "", + Organization: "bettercap devteam", + OrganizationalUnit: "https://bettercap.org/", + CommonName: "bettercap", } - defer certfile.Close() + DefaultSpoofConfig = CertConfig{ + Bits: 4096, + Country: "US", + Locality: "Scottsdale", + Organization: "GoDaddy.com, Inc.", + OrganizationalUnit: "https://certs.godaddy.com/repository/", + CommonName: "Go Daddy Secure Certificate Authority - G2", + } +) - priv, err := rsa.GenerateKey(rand.Reader, 4096) +func CertConfigToModule(prefix string, m *session.SessionModule, defaults CertConfig) { + m.AddParam(session.NewIntParameter(prefix+".certificate.bits", strconv.Itoa(defaults.Bits), + "Number of bits of the RSA private key of the generated HTTPS certificate.")) + m.AddParam(session.NewStringParameter(prefix+".certificate.country", defaults.Country, ".*", + "Country field of the generated HTTPS certificate.")) + m.AddParam(session.NewStringParameter(prefix+".certificate.locality", defaults.Locality, ".*", + "Locality field of the generated HTTPS certificate.")) + m.AddParam(session.NewStringParameter(prefix+".certificate.organization", defaults.Organization, ".*", + "Organization field of the generated HTTPS certificate.")) + m.AddParam(session.NewStringParameter(prefix+".certificate.organizationalunit", defaults.OrganizationalUnit, ".*", + "Organizational Unit field of the generated HTTPS certificate.")) + m.AddParam(session.NewStringParameter(prefix+".certificate.commonname", defaults.CommonName, ".*", + "Common Name field of the generated HTTPS certificate.")) +} + +func CertConfigFromModule(prefix string, m session.SessionModule) (err error, cfg CertConfig) { + if err, cfg.Bits = m.IntParam(prefix + ".certificate.bits"); err != nil { + return err, cfg + } else if err, cfg.Country = m.StringParam(prefix + ".certificate.country"); err != nil { + return err, cfg + } else if err, cfg.Locality = m.StringParam(prefix + ".certificate.locality"); err != nil { + return err, cfg + } else if err, cfg.Organization = m.StringParam(prefix + ".certificate.organization"); err != nil { + return err, cfg + } else if err, cfg.OrganizationalUnit = m.StringParam(prefix + ".certificate.organizationalunit"); err != nil { + return err, cfg + } else if err, cfg.CommonName = m.StringParam(prefix + ".certificate.commonname"); err != nil { + return err, cfg + } + return nil, cfg +} + +func CreateCertificate(cfg CertConfig) (error, *rsa.PrivateKey, []byte) { + priv, err := rsa.GenerateKey(rand.Reader, cfg.Bits) if err != nil { - return err + return err, nil, nil } notBefore := time.Now() @@ -35,17 +86,17 @@ func Generate(certPath string, keyPath string) error { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { - return err + return err, nil, nil } template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Country: []string{"US"}, - Locality: []string{"Scottsdale"}, - Organization: []string{"GoDaddy.com, Inc."}, - OrganizationalUnit: []string{"https://certs.godaddy.com/repository/"}, - CommonName: "Go Daddy Secure Certificate Authority - G2", + Country: []string{cfg.Country}, + Locality: []string{cfg.Locality}, + Organization: []string{cfg.Organization}, + OrganizationalUnit: []string{cfg.OrganizationalUnit}, + CommonName: cfg.CommonName, }, NotBefore: notBefore, NotAfter: notAfter, @@ -55,14 +106,35 @@ func Generate(certPath string, keyPath string) error { IsCA: true, } - cert_raw, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return err, nil, nil + } + + return nil, priv, cert +} + +func Generate(cfg CertConfig, certPath string, keyPath string) error { + keyFile, err := os.Create(keyPath) + if err != nil { + return err + } + defer keyFile.Close() + + certFile, err := os.Create(certPath) + if err != nil { + return err + } + defer certFile.Close() + + err, priv, cert := CreateCertificate(cfg) if err != nil { return err } - if err := pem.Encode(keyfile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil { + if err := pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil { return err } - return pem.Encode(certfile, &pem.Block{Type: "CERTIFICATE", Bytes: cert_raw}) + return pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) }