diff --git a/README.md b/README.md index 06a64f2..77555d2 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,8 @@ Application Options: --connect-timeout= Maximum time in seconds allowed for the connection phase. -I, --head Fetch the headers only. -k, --insecure Disables TLS verification of the connection. + --cacert= Path to custom CA + certificate file. --tlsv1.3 Forces gocurl to use TLS v1.3 or newer. --tlsv1.2 Forces gocurl to use TLS v1.2 or newer. --tls-max= (TLS) VERSION defines maximum supported TLS version. Can be diff --git a/internal/client/clientdialer.go b/internal/client/clientdialer.go index 5eb81b3..2e743ba 100644 --- a/internal/client/clientdialer.go +++ b/internal/client/clientdialer.go @@ -4,9 +4,11 @@ import ( "context" "crypto/rand" "crypto/tls" + "crypto/x509" "fmt" "io" "net" + "os" "time" "github.com/ameshkov/gocurl/internal/client/cfcrypto" @@ -223,6 +225,23 @@ func createTLSConfig(hostname string, cfg *config.Config, out *output.Output) (t tlsConfig.InsecureSkipVerify = true } + // Load CA certificate if specified + if cfg.CACert != "" { + caCert, err := os.ReadFile(cfg.CACert) + if err != nil { + out.Error("Failed to read CA certificate: %v", err) + return nil + } + + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { + out.Error("Failed to parse CA certificate") + return nil + } + + tlsConfig.RootCAs = caCertPool + } + if len(cfg.TLSRandom) == 32 { out.Debug("Overriding TLS ClientHello random value") tlsConfig.Rand = &tlsRandomReader{data: cfg.TLSRandom} diff --git a/internal/config/config.go b/internal/config/config.go index aa71b3a..fdac0e9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "net/url" + "os" "strconv" "strings" @@ -44,6 +45,9 @@ type Config struct { // Insecure disables TLS verification of the connection. Insecure bool + // Provide your own CA certificates + CACert string + // TLSMinVersion is a minimum supported TLS version. TLSMinVersion uint16 @@ -181,6 +185,7 @@ func ParseConfig(args []string) (cfg *Config, err error) { Method: opts.Method, Head: opts.Head, Insecure: opts.Insecure, + CACert: opts.CACert, Data: opts.Data, OutputJSON: opts.OutputJSON, OutputPath: opts.OutputPath, @@ -324,6 +329,14 @@ func ParseConfig(args []string) (cfg *Config, err error) { } } + // Handle CA certificate file if specified + if opts.CACert != "" { + if _, err := os.Stat(opts.CACert); err != nil { + return nil, fmt.Errorf("CA certificate file not found %s: %w", opts.CACert, err) + } + cfg.CACert = opts.CACert + } + return cfg, nil } diff --git a/internal/config/options.go b/internal/config/options.go index 7d14bf0..6c4badf 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -41,6 +41,9 @@ type Options struct { // Insecure disables TLS verification of the connection. Insecure bool `short:"k" long:"insecure" description:"Disables TLS verification of the connection." optional:"yes" optional-value:"true"` + // Provide your own CA certificates + CACert string `long:"cacert" description:"Path to custom CA certificate file." value-name:""` + // TLSv13 forces to use TLS v1.3. TLSv13 bool `long:"tlsv1.3" description:"Forces gocurl to use TLS v1.3 or newer." optional:"yes" optional-value:"true"`