|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- // Copyright 2015 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- // Package mitm provides tooling for MITMing TLS connections. It provides
- // tooling to create CA certs and generate TLS configs that can be used to MITM
- // a TLS connection with a provided CA certificate.
- package mitm
-
- import (
- "bytes"
- "crypto/rand"
- "crypto/rsa"
- "crypto/sha1"
- "crypto/tls"
- "crypto/x509"
- "crypto/x509/pkix"
- "errors"
- "math/big"
- "net"
- "net/http"
- "sync"
- "time"
-
- "github.com/google/martian/log"
- )
-
- // MaxSerialNumber is the upper boundary that is used to create unique serial
- // numbers for the certificate. This can be any unsigned integer up to 20
- // bytes (2^(8*20)-1).
- var MaxSerialNumber = big.NewInt(0).SetBytes(bytes.Repeat([]byte{255}, 20))
-
- // Config is a set of configuration values that are used to build TLS configs
- // capable of MITM.
- type Config struct {
- ca *x509.Certificate
- capriv interface{}
- priv *rsa.PrivateKey
- keyID []byte
- validity time.Duration
- org string
- getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)
- roots *x509.CertPool
- skipVerify bool
- handshakeErrorCallback func(*http.Request, error)
-
- certmu sync.RWMutex
- certs map[string]*tls.Certificate
- }
-
- // NewAuthority creates a new CA certificate and associated
- // private key.
- func NewAuthority(name, organization string, validity time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
- priv, err := rsa.GenerateKey(rand.Reader, 2048)
- if err != nil {
- return nil, nil, err
- }
- pub := priv.Public()
-
- // Subject Key Identifier support for end entity certificate.
- // https://www.ietf.org/rfc/rfc3280.txt (section 4.2.1.2)
- pkixpub, err := x509.MarshalPKIXPublicKey(pub)
- if err != nil {
- return nil, nil, err
- }
- h := sha1.New()
- h.Write(pkixpub)
- keyID := h.Sum(nil)
-
- // TODO: keep a map of used serial numbers to avoid potentially reusing a
- // serial multiple times.
- serial, err := rand.Int(rand.Reader, MaxSerialNumber)
- if err != nil {
- return nil, nil, err
- }
-
- tmpl := &x509.Certificate{
- SerialNumber: serial,
- Subject: pkix.Name{
- CommonName: name,
- Organization: []string{organization},
- },
- SubjectKeyId: keyID,
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
- ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
- BasicConstraintsValid: true,
- NotBefore: time.Now().Add(-validity),
- NotAfter: time.Now().Add(validity),
- DNSNames: []string{name},
- IsCA: true,
- }
-
- raw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, pub, priv)
- if err != nil {
- return nil, nil, err
- }
-
- // Parse certificate bytes so that we have a leaf certificate.
- x509c, err := x509.ParseCertificate(raw)
- if err != nil {
- return nil, nil, err
- }
-
- return x509c, priv, nil
- }
-
- // NewConfig creates a MITM config using the CA certificate and
- // private key to generate on-the-fly certificates.
- func NewConfig(ca *x509.Certificate, privateKey interface{}) (*Config, error) {
- roots := x509.NewCertPool()
- roots.AddCert(ca)
-
- priv, err := rsa.GenerateKey(rand.Reader, 2048)
- if err != nil {
- return nil, err
- }
- pub := priv.Public()
-
- // Subject Key Identifier support for end entity certificate.
- // https://www.ietf.org/rfc/rfc3280.txt (section 4.2.1.2)
- pkixpub, err := x509.MarshalPKIXPublicKey(pub)
- if err != nil {
- return nil, err
- }
- h := sha1.New()
- h.Write(pkixpub)
- keyID := h.Sum(nil)
-
- return &Config{
- ca: ca,
- capriv: privateKey,
- priv: priv,
- keyID: keyID,
- validity: time.Hour,
- org: "Martian Proxy",
- certs: make(map[string]*tls.Certificate),
- roots: roots,
- }, nil
- }
-
- // SetValidity sets the validity window around the current time that the
- // certificate is valid for.
- func (c *Config) SetValidity(validity time.Duration) {
- c.validity = validity
- }
-
- // SkipTLSVerify skips the TLS certification verification check.
- func (c *Config) SkipTLSVerify(skip bool) {
- c.skipVerify = skip
- }
-
- // SetOrganization sets the organization of the certificate.
- func (c *Config) SetOrganization(org string) {
- c.org = org
- }
-
- // SetHandshakeErrorCallback sets the handshakeErrorCallback function.
- func (c *Config) SetHandshakeErrorCallback(cb func(*http.Request, error)) {
- c.handshakeErrorCallback = cb
- }
-
- // HandshakeErrorCallback calls the handshakeErrorCallback function in this
- // Config, if it is non-nil. Request is the connect request that this handshake
- // is being executed through.
- func (c *Config) HandshakeErrorCallback(r *http.Request, err error) {
- if c.handshakeErrorCallback != nil {
- c.handshakeErrorCallback(r, err)
- }
- }
-
- // TLS returns a *tls.Config that will generate certificates on-the-fly using
- // the SNI extension in the TLS ClientHello.
- func (c *Config) TLS() *tls.Config {
- return &tls.Config{
- InsecureSkipVerify: c.skipVerify,
- GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
- if clientHello.ServerName == "" {
- return nil, errors.New("mitm: SNI not provided, failed to build certificate")
- }
-
- return c.cert(clientHello.ServerName)
- },
- NextProtos: []string{"http/1.1"},
- }
- }
-
- // TLSForHost returns a *tls.Config that will generate certificates on-the-fly
- // using SNI from the connection, or fall back to the provided hostname.
- func (c *Config) TLSForHost(hostname string) *tls.Config {
- return &tls.Config{
- InsecureSkipVerify: c.skipVerify,
- GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
- host := clientHello.ServerName
- if host == "" {
- host = hostname
- }
-
- return c.cert(host)
- },
- NextProtos: []string{"http/1.1"},
- }
- }
-
- func (c *Config) cert(hostname string) (*tls.Certificate, error) {
- // Remove the port if it exists.
- host, _, err := net.SplitHostPort(hostname)
- if err == nil {
- hostname = host
- }
-
- c.certmu.RLock()
- tlsc, ok := c.certs[hostname]
- c.certmu.RUnlock()
-
- if ok {
- log.Debugf("mitm: cache hit for %s", hostname)
-
- // Check validity of the certificate for hostname match, expiry, etc. In
- // particular, if the cached certificate has expired, create a new one.
- if _, err := tlsc.Leaf.Verify(x509.VerifyOptions{
- DNSName: hostname,
- Roots: c.roots,
- }); err == nil {
- return tlsc, nil
- }
-
- log.Debugf("mitm: invalid certificate in cache for %s", hostname)
- }
-
- log.Debugf("mitm: cache miss for %s", hostname)
-
- serial, err := rand.Int(rand.Reader, MaxSerialNumber)
- if err != nil {
- return nil, err
- }
-
- tmpl := &x509.Certificate{
- SerialNumber: serial,
- Subject: pkix.Name{
- CommonName: hostname,
- Organization: []string{c.org},
- },
- SubjectKeyId: c.keyID,
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
- ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
- BasicConstraintsValid: true,
- NotBefore: time.Now().Add(-c.validity),
- NotAfter: time.Now().Add(c.validity),
- }
-
- if ip := net.ParseIP(hostname); ip != nil {
- tmpl.IPAddresses = []net.IP{ip}
- } else {
- tmpl.DNSNames = []string{hostname}
- }
-
- raw, err := x509.CreateCertificate(rand.Reader, tmpl, c.ca, c.priv.Public(), c.capriv)
- if err != nil {
- return nil, err
- }
-
- // Parse certificate bytes so that we have a leaf certificate.
- x509c, err := x509.ParseCertificate(raw)
- if err != nil {
- return nil, err
- }
-
- tlsc = &tls.Certificate{
- Certificate: [][]byte{raw, c.ca.Raw},
- PrivateKey: c.priv,
- Leaf: x509c,
- }
-
- c.certmu.Lock()
- c.certs[hostname] = tlsc
- c.certmu.Unlock()
-
- return tlsc, nil
- }
|