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 }