http_signer.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. All rights reserved.
  2. // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
  3. package common
  4. import (
  5. "bytes"
  6. "crypto"
  7. "crypto/rand"
  8. "crypto/rsa"
  9. "crypto/sha256"
  10. "encoding/base64"
  11. "fmt"
  12. "io"
  13. "io/ioutil"
  14. "net/http"
  15. "strings"
  16. )
  17. // HTTPRequestSigner the interface to sign a request
  18. type HTTPRequestSigner interface {
  19. Sign(r *http.Request) error
  20. }
  21. // KeyProvider interface that wraps information about the key's account owner
  22. type KeyProvider interface {
  23. PrivateRSAKey() (*rsa.PrivateKey, error)
  24. KeyID() (string, error)
  25. }
  26. const signerVersion = "1"
  27. // SignerBodyHashPredicate a function that allows to disable/enable body hashing
  28. // of requests and headers associated with body content
  29. type SignerBodyHashPredicate func(r *http.Request) bool
  30. // ociRequestSigner implements the http-signatures-draft spec
  31. // as described in https://tools.ietf.org/html/draft-cavage-http-signatures-08
  32. type ociRequestSigner struct {
  33. KeyProvider KeyProvider
  34. GenericHeaders []string
  35. BodyHeaders []string
  36. ShouldHashBody SignerBodyHashPredicate
  37. }
  38. var (
  39. defaultGenericHeaders = []string{"date", "(request-target)", "host"}
  40. defaultBodyHeaders = []string{"content-length", "content-type", "x-content-sha256"}
  41. defaultBodyHashPredicate = func(r *http.Request) bool {
  42. return r.Method == http.MethodPost || r.Method == http.MethodPut || r.Method == http.MethodPatch
  43. }
  44. )
  45. // DefaultGenericHeaders list of default generic headers that is used in signing
  46. func DefaultGenericHeaders() []string {
  47. return makeACopy(defaultGenericHeaders)
  48. }
  49. // DefaultBodyHeaders list of default body headers that is used in signing
  50. func DefaultBodyHeaders() []string {
  51. return makeACopy(defaultBodyHeaders)
  52. }
  53. // DefaultRequestSigner creates a signer with default parameters.
  54. func DefaultRequestSigner(provider KeyProvider) HTTPRequestSigner {
  55. return RequestSigner(provider, defaultGenericHeaders, defaultBodyHeaders)
  56. }
  57. // RequestSignerExcludeBody creates a signer without hash the body.
  58. func RequestSignerExcludeBody(provider KeyProvider) HTTPRequestSigner {
  59. bodyHashPredicate := func(r *http.Request) bool {
  60. // week request signer will not hash the body
  61. return false
  62. }
  63. return RequestSignerWithBodyHashingPredicate(provider, defaultGenericHeaders, defaultBodyHeaders, bodyHashPredicate)
  64. }
  65. // NewSignerFromOCIRequestSigner creates a copy of the request signer and attaches the new SignerBodyHashPredicate
  66. // returns an error if the passed signer is not of type ociRequestSigner
  67. func NewSignerFromOCIRequestSigner(oldSigner HTTPRequestSigner, predicate SignerBodyHashPredicate) (HTTPRequestSigner, error) {
  68. if oldS, ok := oldSigner.(ociRequestSigner); ok {
  69. s := ociRequestSigner{
  70. KeyProvider: oldS.KeyProvider,
  71. GenericHeaders: oldS.GenericHeaders,
  72. BodyHeaders: oldS.BodyHeaders,
  73. ShouldHashBody: predicate,
  74. }
  75. return s, nil
  76. }
  77. return nil, fmt.Errorf("can not create a signer, input signer needs to be of type ociRequestSigner")
  78. }
  79. // RequestSigner creates a signer that utilizes the specified headers for signing
  80. // and the default predicate for using the body of the request as part of the signature
  81. func RequestSigner(provider KeyProvider, genericHeaders, bodyHeaders []string) HTTPRequestSigner {
  82. return ociRequestSigner{
  83. KeyProvider: provider,
  84. GenericHeaders: genericHeaders,
  85. BodyHeaders: bodyHeaders,
  86. ShouldHashBody: defaultBodyHashPredicate}
  87. }
  88. // RequestSignerWithBodyHashingPredicate creates a signer that utilizes the specified headers for signing, as well as a predicate for using
  89. // the body of the request and bodyHeaders parameter as part of the signature
  90. func RequestSignerWithBodyHashingPredicate(provider KeyProvider, genericHeaders, bodyHeaders []string, shouldHashBody SignerBodyHashPredicate) HTTPRequestSigner {
  91. return ociRequestSigner{
  92. KeyProvider: provider,
  93. GenericHeaders: genericHeaders,
  94. BodyHeaders: bodyHeaders,
  95. ShouldHashBody: shouldHashBody}
  96. }
  97. func (signer ociRequestSigner) getSigningHeaders(r *http.Request) []string {
  98. var result []string
  99. result = append(result, signer.GenericHeaders...)
  100. if signer.ShouldHashBody(r) {
  101. result = append(result, signer.BodyHeaders...)
  102. }
  103. return result
  104. }
  105. func (signer ociRequestSigner) getSigningString(request *http.Request) string {
  106. signingHeaders := signer.getSigningHeaders(request)
  107. signingParts := make([]string, len(signingHeaders))
  108. for i, part := range signingHeaders {
  109. var value string
  110. part = strings.ToLower(part)
  111. switch part {
  112. case "(request-target)":
  113. value = getRequestTarget(request)
  114. case "host":
  115. value = request.URL.Host
  116. if len(value) == 0 {
  117. value = request.Host
  118. }
  119. default:
  120. value = request.Header.Get(part)
  121. }
  122. signingParts[i] = fmt.Sprintf("%s: %s", part, value)
  123. }
  124. signingString := strings.Join(signingParts, "\n")
  125. return signingString
  126. }
  127. func getRequestTarget(request *http.Request) string {
  128. lowercaseMethod := strings.ToLower(request.Method)
  129. return fmt.Sprintf("%s %s", lowercaseMethod, request.URL.RequestURI())
  130. }
  131. func calculateHashOfBody(request *http.Request) (err error) {
  132. var hash string
  133. hash, err = GetBodyHash(request)
  134. if err != nil {
  135. return
  136. }
  137. request.Header.Set(requestHeaderXContentSHA256, hash)
  138. return
  139. }
  140. // drainBody reads all of b to memory and then returns two equivalent
  141. // ReadClosers yielding the same bytes.
  142. //
  143. // It returns an error if the initial slurp of all bytes fails. It does not attempt
  144. // to make the returned ReadClosers have identical error-matching behavior.
  145. func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
  146. if b == http.NoBody {
  147. // No copying needed. Preserve the magic sentinel meaning of NoBody.
  148. return http.NoBody, http.NoBody, nil
  149. }
  150. var buf bytes.Buffer
  151. if _, err = buf.ReadFrom(b); err != nil {
  152. return nil, b, err
  153. }
  154. if err = b.Close(); err != nil {
  155. return nil, b, err
  156. }
  157. return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
  158. }
  159. func hashAndEncode(data []byte) string {
  160. hashedContent := sha256.Sum256(data)
  161. hash := base64.StdEncoding.EncodeToString(hashedContent[:])
  162. return hash
  163. }
  164. // GetBodyHash creates a base64 string from the hash of body the request
  165. func GetBodyHash(request *http.Request) (hashString string, err error) {
  166. if request.Body == nil {
  167. request.ContentLength = 0
  168. request.Header.Set(requestHeaderContentLength, fmt.Sprintf("%v", request.ContentLength))
  169. return hashAndEncode([]byte("")), nil
  170. }
  171. var data []byte
  172. bReader := request.Body
  173. bReader, request.Body, err = drainBody(request.Body)
  174. if err != nil {
  175. return "", fmt.Errorf("can not read body of request while calculating body hash: %s", err.Error())
  176. }
  177. data, err = ioutil.ReadAll(bReader)
  178. if err != nil {
  179. return "", fmt.Errorf("can not read body of request while calculating body hash: %s", err.Error())
  180. }
  181. // Since the request can be coming from a binary body. Make an attempt to set the body length
  182. request.ContentLength = int64(len(data))
  183. request.Header.Set(requestHeaderContentLength, fmt.Sprintf("%v", request.ContentLength))
  184. hashString = hashAndEncode(data)
  185. return
  186. }
  187. func (signer ociRequestSigner) computeSignature(request *http.Request) (signature string, err error) {
  188. signingString := signer.getSigningString(request)
  189. hasher := sha256.New()
  190. hasher.Write([]byte(signingString))
  191. hashed := hasher.Sum(nil)
  192. privateKey, err := signer.KeyProvider.PrivateRSAKey()
  193. if err != nil {
  194. return
  195. }
  196. var unencodedSig []byte
  197. unencodedSig, e := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
  198. if e != nil {
  199. err = fmt.Errorf("can not compute signature while signing the request %s: ", e.Error())
  200. return
  201. }
  202. signature = base64.StdEncoding.EncodeToString(unencodedSig)
  203. return
  204. }
  205. // Sign signs the http request, by inspecting the necessary headers. Once signed
  206. // the request will have the proper 'Authorization' header set, otherwise
  207. // and error is returned
  208. func (signer ociRequestSigner) Sign(request *http.Request) (err error) {
  209. if signer.ShouldHashBody(request) {
  210. err = calculateHashOfBody(request)
  211. if err != nil {
  212. return
  213. }
  214. }
  215. var signature string
  216. if signature, err = signer.computeSignature(request); err != nil {
  217. return
  218. }
  219. signingHeaders := strings.Join(signer.getSigningHeaders(request), " ")
  220. var keyID string
  221. if keyID, err = signer.KeyProvider.KeyID(); err != nil {
  222. return
  223. }
  224. authValue := fmt.Sprintf("Signature version=\"%s\",headers=\"%s\",keyId=\"%s\",algorithm=\"rsa-sha256\",signature=\"%s\"",
  225. signerVersion, signingHeaders, keyID, signature)
  226. request.Header.Set(requestHeaderAuthorization, authValue)
  227. return
  228. }