github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_request_signing.go (about)

     1  package gateway
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/sha256"
     8  	"encoding/base64"
     9  	"errors"
    10  	"hash"
    11  	"net/http"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/TykTechnologies/tyk/certs"
    16  )
    17  
    18  type RequestSigning struct {
    19  	BaseMiddleware
    20  }
    21  
    22  func (s *RequestSigning) Name() string {
    23  	return "RequestSigning"
    24  }
    25  
    26  func (s *RequestSigning) EnabledForSpec() bool {
    27  	return s.Spec.RequestSigning.IsEnabled
    28  }
    29  
    30  var supportedAlgorithms = []string{"hmac-sha1", "hmac-sha256", "hmac-sha384", "hmac-sha512", "rsa-sha256"}
    31  
    32  func generateHeaderList(r *http.Request, headerList []string) []string {
    33  	var result []string
    34  
    35  	if len(headerList) == 0 {
    36  		result = make([]string, len(r.Header)+1)
    37  		result[0] = "(request-target)"
    38  		i := 1
    39  
    40  		for k := range r.Header {
    41  			loweredCaseHeader := strings.ToLower(k)
    42  			result[i] = strings.TrimSpace(loweredCaseHeader)
    43  			i++
    44  		}
    45  
    46  		// date header is must as per Signing HTTP Messages Draft
    47  		if r.Header.Get("date") == "" {
    48  			refDate := "Mon, 02 Jan 2006 15:04:05 MST"
    49  			tim := time.Now().Format(refDate)
    50  
    51  			r.Header.Set("date", tim)
    52  			result = append(result, "date")
    53  		}
    54  	} else {
    55  		result = make([]string, 0, len(headerList))
    56  
    57  		for _, v := range headerList {
    58  			if r.Header.Get(v) != "" {
    59  				result = append(result, v)
    60  			}
    61  		}
    62  
    63  		if len(result) == 0 {
    64  			headers := []string{"(request-target)", "date"}
    65  			result = append(result, headers...)
    66  
    67  			if r.Header.Get("date") == "" {
    68  				refDate := "Mon, 02 Jan 2006 15:04:05 MST"
    69  				tim := time.Now().Format(refDate)
    70  				r.Header.Set("date", tim)
    71  			}
    72  		}
    73  	}
    74  
    75  	return result
    76  }
    77  
    78  func (s *RequestSigning) getRequestPath(r *http.Request) string {
    79  	path := r.URL.RequestURI()
    80  
    81  	if newURL := ctxGetURLRewriteTarget(r); newURL != nil {
    82  		path = newURL.RequestURI()
    83  	} else {
    84  		if s.Spec.Proxy.StripListenPath {
    85  			path = s.Spec.StripListenPath(r, path)
    86  			if !strings.HasPrefix(path, "/") {
    87  				path = "/" + path
    88  			}
    89  		}
    90  	}
    91  
    92  	return path
    93  }
    94  
    95  func (s *RequestSigning) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
    96  	if (s.Spec.RequestSigning.Secret == "" && s.Spec.RequestSigning.CertificateId == "") || s.Spec.RequestSigning.KeyId == "" || s.Spec.RequestSigning.Algorithm == "" {
    97  		log.Error("Fields required for signing the request are missing")
    98  		return errors.New("Fields required for signing the request are missing"), http.StatusInternalServerError
    99  	}
   100  
   101  	var algoList []string
   102  	if len(s.Spec.HmacAllowedAlgorithms) > 0 {
   103  		algoList = s.Spec.HmacAllowedAlgorithms
   104  	} else {
   105  		algoList = supportedAlgorithms
   106  	}
   107  
   108  	algorithmAllowed := false
   109  	for _, alg := range algoList {
   110  		if alg == s.Spec.RequestSigning.Algorithm {
   111  			algorithmAllowed = true
   112  			break
   113  		}
   114  	}
   115  	if !algorithmAllowed {
   116  		log.WithField("algorithm", s.Spec.RequestSigning.Algorithm).Error("Algorithm not supported")
   117  		return errors.New("Request signing algorithm is not supported"), http.StatusInternalServerError
   118  	}
   119  
   120  	headers := generateHeaderList(r, s.Spec.RequestSigning.HeaderList)
   121  
   122  	path := s.getRequestPath(r)
   123  
   124  	signatureString, err := generateHMACSignatureStringFromRequest(r, headers, path)
   125  	if err != nil {
   126  		log.Error(err)
   127  		return err, http.StatusInternalServerError
   128  	}
   129  	strHeaders := strings.Join(headers, " ")
   130  
   131  	var encodedSignature string
   132  
   133  	if strings.HasPrefix(s.Spec.RequestSigning.Algorithm, "rsa") {
   134  		if s.Spec.RequestSigning.CertificateId == "" {
   135  			log.Error("CertificateID is empty")
   136  			return errors.New("CertificateID is empty"), http.StatusInternalServerError
   137  		}
   138  
   139  		certList := CertificateManager.List([]string{s.Spec.RequestSigning.CertificateId}, certs.CertificatePrivate)
   140  		if len(certList) == 0 || certList[0] == nil {
   141  			log.Error("Certificate not found")
   142  			return errors.New("Certificate not found"), http.StatusInternalServerError
   143  		}
   144  		cert := certList[0]
   145  		rsaKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
   146  		if !ok {
   147  			log.Error("Certificate does not contain RSA private key")
   148  			return errors.New("Certificate does not contain RSA private key"), http.StatusInternalServerError
   149  		}
   150  		encodedSignature, err = generateRSAEncodedSignature(signatureString, rsaKey, s.Spec.RequestSigning.Algorithm)
   151  		if err != nil {
   152  			log.Error("Error while generating signature:", err)
   153  			return err, http.StatusInternalServerError
   154  		}
   155  	} else {
   156  		var err error
   157  		encodedSignature, err = generateHMACEncodedSignature(signatureString, s.Spec.RequestSigning.Secret, s.Spec.RequestSigning.Algorithm)
   158  		if err != nil {
   159  			return err, http.StatusInternalServerError
   160  		}
   161  	}
   162  
   163  	//Generate Authorization header
   164  	authHeader := "Signature "
   165  	//Append keyId
   166  	authHeader += "keyId=\"" + s.Spec.RequestSigning.KeyId + "\","
   167  	//Append algorithm
   168  	authHeader += "algorithm=\"" + s.Spec.RequestSigning.Algorithm + "\","
   169  	//Append Headers
   170  	authHeader += "headers=\"" + strHeaders + "\","
   171  	//Append signature
   172  	authHeader += "signature=\"" + encodedSignature + "\""
   173  
   174  	if s.Spec.RequestSigning.SignatureHeader != "" {
   175  		r.Header.Set(s.Spec.RequestSigning.SignatureHeader, authHeader)
   176  		log.Debugf("Setting %s headers as =%s", s.Spec.RequestSigning.SignatureHeader, authHeader)
   177  	} else {
   178  		r.Header.Set("Authorization", authHeader)
   179  		log.Debug("Setting Authorization headers as =", authHeader)
   180  	}
   181  
   182  	return nil, http.StatusOK
   183  }
   184  
   185  func generateRSAEncodedSignature(signatureString string, privateKey *rsa.PrivateKey, algorithm string) (string, error) {
   186  	var hashFunction hash.Hash
   187  	var hashType crypto.Hash
   188  
   189  	switch algorithm {
   190  	case "rsa-sha256":
   191  		hashFunction = sha256.New()
   192  		hashType = crypto.SHA256
   193  	default:
   194  		hashFunction = sha256.New()
   195  		hashType = crypto.SHA256
   196  	}
   197  	hashFunction.Write([]byte(signatureString))
   198  	hashed := hashFunction.Sum(nil)
   199  
   200  	rawsignature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, hashType, hashed)
   201  	if err != nil {
   202  		return "", err
   203  	}
   204  	encodedSignature := base64.StdEncoding.EncodeToString(rawsignature)
   205  
   206  	return encodedSignature, nil
   207  }