diff options
author | heqnx <root@heqnx.com> | 2025-05-12 13:30:08 +0300 |
---|---|---|
committer | heqnx <root@heqnx.com> | 2025-05-12 13:30:08 +0300 |
commit | 19e6b451f0221384bcb4c62ddbff1aa9293d3595 (patch) | |
tree | b8c9f211c71c28bc12cbca98c97e6ba5d58857b2 /main.go | |
parent | 96dae669b105310198c26c8b300bf69b7a779826 (diff) | |
download | go-fakessl-19e6b451f0221384bcb4c62ddbff1aa9293d3595.tar.gz go-fakessl-19e6b451f0221384bcb4c62ddbff1aa9293d3595.zip |
initial commit on go-fakessl
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 165 |
1 files changed, 165 insertions, 0 deletions
@@ -0,0 +1,165 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "flag" + "fmt" + "log" + "net" + "net/http" + "net/url" + "os" + "text/tabwriter" +) + +func cloneCertificateTemplate(orig *x509.Certificate) *x509.Certificate { + return &x509.Certificate{ + SerialNumber: orig.SerialNumber, + SignatureAlgorithm: orig.SignatureAlgorithm, + Issuer: orig.Issuer, + Subject: orig.Subject, + NotBefore: orig.NotBefore, + NotAfter: orig.NotAfter, + KeyUsage: orig.KeyUsage, + ExtKeyUsage: orig.ExtKeyUsage, + UnknownExtKeyUsage: orig.UnknownExtKeyUsage, + BasicConstraintsValid: orig.BasicConstraintsValid, + IsCA: orig.IsCA, + MaxPathLen: orig.MaxPathLen, + MaxPathLenZero: orig.MaxPathLenZero, + DNSNames: orig.DNSNames, + EmailAddresses: orig.EmailAddresses, + IPAddresses: orig.IPAddresses, + URIs: orig.URIs, + PolicyIdentifiers: orig.PolicyIdentifiers, + CRLDistributionPoints: orig.CRLDistributionPoints, + OCSPServer: orig.OCSPServer, + IssuingCertificateURL: orig.IssuingCertificateURL, + ExtraExtensions: orig.Extensions, + SubjectKeyId: orig.SubjectKeyId, + AuthorityKeyId: orig.AuthorityKeyId, + } +} + +func cloneCertificate(urlStr string) (certFilename, keyFilename string) { + writer := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', tabwriter.AlignRight) + + parsedURL, err := url.Parse(urlStr) + if err != nil { + log.Fatalf("[err] invalid url: %v", err) + } + + host := parsedURL.Host + if parsedURL.Port() == "" { + host = host + ":443" + } + + domain := parsedURL.Host + if h, _, err := net.SplitHostPort(parsedURL.Host); err == nil { + domain = h + } + + certFilename = domain + "_clone.pem" + keyFilename = domain + "_clone.key" + + conn, err := tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + log.Fatalf("[err] failed to connect to %s: %v", host, err) + } + defer conn.Close() + + state := conn.ConnectionState() + if len(state.PeerCertificates) == 0 { + log.Fatal("[err] no certificates found on the target server") + } + origCert := state.PeerCertificates[0] + + template := cloneCertificateTemplate(origCert) + + rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatalf("[err] failed to generate rsa key: %v", err) + } + priv := rsaKey + pub := &rsaKey.PublicKey + + parent := cloneCertificateTemplate(origCert) + parent.Subject = origCert.Issuer + + certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv) + if err != nil { + log.Fatalf("[err] failed to create fake certificate: %v", err) + } + + certOut, err := os.Create(certFilename) + if err != nil { + log.Fatalf("[err] failed to open %s for writing: %v", certFilename, err) + } + defer certOut.Close() + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}); err != nil { + log.Fatalf("[err] failed to write certificate: %v", err) + } + + keyOut, err := os.Create(keyFilename) + if err != nil { + log.Fatalf("[err] failed to open %s for writing: %v", keyFilename, err) + } + defer keyOut.Close() + if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaKey)}); err != nil { + log.Fatalf("[err] failed to write private key: %v", err) + } + + fmt.Fprintln(writer, "url\tcloned cert\tprivate key") + fmt.Fprintf(writer, "%s\t%s\t%s", host, certFilename, keyFilename) + writer.Flush() + fmt.Println() + fmt.Println() + fmt.Println("[inf] start an https server to test cloned certificate with:") + fmt.Printf("$ %s -cert %s -key %s -port 8000\n", os.Args[0], certFilename, keyFilename) + fmt.Println() + fmt.Println("[inf] manually inspect and diff the original certificate and cloned certificate with:") + fmt.Printf("$ openssl s_client -connect %s </dev/null 2>/dev/null | openssl x509 -noout -text > %s_original.txt\n", host, parsedURL.Host) + fmt.Printf("$ openssl x509 -in %s -noout -text > %s_clone.txt\n", certFilename, certFilename) + fmt.Println("$ diff *.txt") + return +} + +func runHTTPSServer(certPath, keyPath string, portNumber string) { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, world!") + }) + fmt.Printf("[inf] starting https server on https://127.0.0.1:%s\n", portNumber) + err := http.ListenAndServeTLS("127.0.0.1:"+portNumber, certPath, keyPath, nil) + if err != nil { + log.Fatalf("[err] failed to start httpsserver: %v", err) + } +} + +func main() { + urlFlag := flag.String("url", "", "target https url to clone certificate from (e.g. https://google.com)") + certFlag := flag.String("cert", "", "path to certificate file to use for a test https server") + keyFlag := flag.String("key", "", "path to key file to use for a test https server") + portFlag := flag.String("port", "8000", "port to use for a test https server") + flag.Parse() + + if *certFlag != "" || *keyFlag != "" { + if *certFlag == "" || *keyFlag == "" { + log.Fatal("[err] both -cert and -key must be supplied to run the HTTPS server") + } + runHTTPSServer(*certFlag, *keyFlag, *portFlag) + return + } + + if *urlFlag != "" { + cloneCertificate(*urlFlag) + return + } + + flag.Usage() + os.Exit(1) +} + |