idnits 2.17.1 draft-rundgren-signed-http-requests-01.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- == There are 1 instance of lines with non-RFC2606-compliant FQDNs in the document. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (September 8, 2019) is 1691 days in the past. Is this intentional? Checking references for intended status: Informational ---------------------------------------------------------------------------- == Missing Reference: 'REST' is mentioned on line 1123, but not defined == Missing Reference: 'HTTPSIG' is mentioned on line 1284, but not defined == Missing Reference: 'AWS' is mentioned on line 1276, but not defined == Missing Reference: 'FAPI' is mentioned on line 1314, but not defined == Missing Reference: 'OBIE' is mentioned on line 1289, but not defined == Missing Reference: 'STET' is mentioned on line 1284, but not defined -- Looks like a reference, but probably isn't: '1' on line 1333 == Outdated reference: A later version (-17) exists of draft-rundgren-json-canonicalization-scheme-05 ** Obsolete normative reference: RFC 7230 (Obsoleted by RFC 9110, RFC 9112) ** Obsolete normative reference: RFC 7231 (Obsoleted by RFC 9110) Summary: 2 errors (**), 0 flaws (~~), 9 warnings (==), 2 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Network Working Group A. Rundgren 3 Internet-Draft Independent 4 Intended status: Informational September 8, 2019 5 Expires: March 11, 2020 7 Signed HTTP Requests (SHREQ) 8 draft-rundgren-signed-http-requests-01 10 Abstract 12 The SHREQ specification describes how the JSON Web Signature (JWS) 13 specification combined with the JSON Canonicalization Scheme (JCS), 14 can be utilized to support HTTP based applications needing digitally 15 signed requests. SHREQ is specifically tailored for Web applications 16 using JSON as data interchange format. There is also a SHREQ scheme 17 for HTTP requests that do not have a body ("payload") like GET. 18 SHREQ was designed to be agnostic with respect to REST concepts 19 versus traditional GET/POST schemes. 21 Status of This Memo 23 This Internet-Draft is submitted in full conformance with the 24 provisions of BCP 78 and BCP 79. 26 Internet-Drafts are working documents of the Internet Engineering 27 Task Force (IETF). Note that other groups may also distribute 28 working documents as Internet-Drafts. The list of current Internet- 29 Drafts is at https://datatracker.ietf.org/drafts/current/. 31 Internet-Drafts are draft documents valid for a maximum of six months 32 and may be updated, replaced, or obsoleted by other documents at any 33 time. It is inappropriate to use Internet-Drafts as reference 34 material or to cite them other than as "work in progress." 36 This Internet-Draft will expire on March 11, 2020. 38 Copyright Notice 40 Copyright (c) 2019 IETF Trust and the persons identified as the 41 document authors. All rights reserved. 43 This document is subject to BCP 78 and the IETF Trust's Legal 44 Provisions Relating to IETF Documents 45 (https://trustee.ietf.org/license-info) in effect on the date of 46 publication of this document. Please review these documents 47 carefully, as they describe your rights and restrictions with respect 48 to this document. Code Components extracted from this document must 49 include Simplified BSD License text as described in Section 4.e of 50 the Trust Legal Provisions and are provided without warranty as 51 described in the Simplified BSD License. 53 Table of Contents 55 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 56 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4 57 3. HTTP Processing . . . . . . . . . . . . . . . . . . . . . . . 4 58 3.1. Determining Request Type . . . . . . . . . . . . . . . . 4 59 3.2. Return Codes . . . . . . . . . . . . . . . . . . . . . . 5 60 4. Processing of JSON Based Requests . . . . . . . . . . . . . . 5 61 4.1. Request Creation . . . . . . . . . . . . . . . . . . . . 6 62 4.2. Request Validation . . . . . . . . . . . . . . . . . . . 8 63 5. Processing of URI Based Requests . . . . . . . . . . . . . . 9 64 5.1. Request Creation . . . . . . . . . . . . . . . . . . . . 10 65 5.2. Request Validation . . . . . . . . . . . . . . . . . . . 11 66 6. Common Operations . . . . . . . . . . . . . . . . . . . . . . 13 67 6.1. Create Signable JSON Data . . . . . . . . . . . . . . . . 13 68 6.2. Create Signable URI Data . . . . . . . . . . . . . . . . 13 69 6.3. Create HTTP Header Object . . . . . . . . . . . . . . . . 14 70 6.4. Create JWS Protected Header . . . . . . . . . . . . . . . 15 71 6.5. Create JWS String . . . . . . . . . . . . . . . . . . . . 15 72 6.6. Decode JWS String . . . . . . . . . . . . . . . . . . . . 16 73 6.7. Normalize Target URI . . . . . . . . . . . . . . . . . . 16 74 6.8. Normalize Header Data . . . . . . . . . . . . . . . . . . 17 75 6.9. Validate HTTP Header Object . . . . . . . . . . . . . . . 18 76 6.10. Validate JWS Signature . . . . . . . . . . . . . . . . . 19 77 6.11. Time Stamps . . . . . . . . . . . . . . . . . . . . . . . 20 78 6.12. Hash Algorithms . . . . . . . . . . . . . . . . . . . . . 20 79 7. Local Naming Conventions . . . . . . . . . . . . . . . . . . 21 80 8. Attachments . . . . . . . . . . . . . . . . . . . . . . . . . 21 81 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 21 82 10. Security Considerations . . . . . . . . . . . . . . . . . . . 22 83 11. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 22 84 12. References . . . . . . . . . . . . . . . . . . . . . . . . . 22 85 12.1. Normative References . . . . . . . . . . . . . . . . . . 22 86 12.2. Informal References . . . . . . . . . . . . . . . . . . 24 87 Appendix A. Test Vectors . . . . . . . . . . . . . . . . . . . . 24 88 A.1. Type=URI, Method=GET, Algorithm=HS256 . . . . . . . . . . 24 89 A.2. Type=JSON, Method=POST, Algorithm=ES256 . . . . . . . . . 25 90 A.3. Type=JSON, Method=PUT, Algorithm=ES256 . . . . . . . . . 25 91 A.4. Type=URI, Method=DELETE, Algorithm=RS256 . . . . . . . . 26 92 Appendix B. Other Signed HTTP Request Solutions . . . . . . . . 27 93 B.1. Amazon Web Services . . . . . . . . . . . . . . . . . . . 27 94 B.2. HTTP Signatures . . . . . . . . . . . . . . . . . . . . . 27 95 B.3. Open Banking (UK) . . . . . . . . . . . . . . . . . . . . 28 96 B.4. Financial API . . . . . . . . . . . . . . . . . . . . . . 28 98 Appendix C. Development Portal . . . . . . . . . . . . . . . . . 28 99 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 29 101 1. Introduction 103 Currently there is no standard for digitally signing HTTP [RFC7230] 104 [RFC7231] requests. This has led to the development of a multitude 105 of more or less proprietary solutions (see Appendix B), typically 106 building on using HTTP header data for holding security constructs, 107 while JSON request data is provided in clear in the HTTP body. 109 SHREQ is intended to provide a standardized alternative, including 110 supporting the REST [REST] concept. 112 SHREQ builds on a common security model where all elements of an HTTP 113 request are signed: 115 o HTTP URI. 117 o HTTP method. 119 o HTTP body (if applicable). 121 o Optional: Additional HTTP headers as defined by applications 122 implementing this specification. 124 In addition there is a mandatory time stamp. 126 One of the design goals was turning signed requests into self- 127 contained objects. To achieve this for HTTP requests having a JSON 128 [RFC8259] body (see Section 4), the request data also carries the 129 signature. This arrangement has certain implications: 131 o Signed requests may be stored in databases or be embedded in other 132 JSON objects. The latter includes supporting counter signatures. 133 The canonicalization offered by JCS [JCS] enables validating the 134 integrity of request data at any time. 136 o For general interoperability concerns as well as due to the 137 reliance on JCS, JSON request data is limited to the I-JSON 138 [RFC7493] subset. 140 For HTTP requests that do not have a JSON body (see Section 5), the 141 signature and additional request data is added to the original URI 142 [RFC3986], making signed URI-only requests self-contained and 143 serializable as well. For simplicity such requests are (in this 144 specification NB), referred to as URI based requests. 146 Both variants utilize JWS [RFC7515] for holding the signature data. 148 For supporting signed HTTP responses any solution may be used. For 149 maximum "symmetry" and code reuse, the [JWSJCS] scheme should be a 150 suitable candidate since it builds on the same building blocks as 151 SHREQ. 153 The intended audiences of this document are Web tool vendors, as well 154 as designers of secure Web applications. 156 2. Terminology 158 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 159 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 160 "OPTIONAL" in this document are to be interpreted as described in BCP 161 14 [RFC2119] [RFC8174] when, and only when, they appear in all 162 capitals, as shown here. 164 3. HTTP Processing 166 The following subsections describe HTTP specifics associated with 167 this specification. 169 3.1. Determining Request Type 171 In this specification the distinction between HTTP requests having a 172 JSON body or not is based on the presence of a "Content-Length" 173 header. Requests without a body object are in this specification 174 referred to as URI based requests. 176 This also implies that not all header combinations permitted by HTTP 177 can be used with this specification: 179 "Content-Length" 180 MUST NOT be used with URI based requests. MUST be present for 181 requests having a body and have an argument holding the length of 182 the body in bytes. 184 "Content-Type" 185 MUST NOT be used with URI based requests. MUST be present for 186 requests having a body and have the argument "application/json". 188 "Content-Encoding" 189 MUST NOT be used with any requests targeting this specification. 191 "Transfer-Encoding" 192 MUST NOT be used with any requests targeting this specification. 194 3.2. Return Codes 196 This specification utilizes a single HTTP return code 400 (Bad 197 request) for indicating syntax or security errors. Since the number 198 of possible error conditions is significant, it is RECOMMENDED to 199 accompany the error code with a short explanation in "text/plain" 200 format in the HTTP Body like: 202 - Missing ".secinf" element 203 - Invalid "alg": es256 204 - Unknown "kid": example.com:rsa:2018.1 205 - com.example.jose.Core.validate(2653): Signature validation error 206 - Missing header variable "x-testing" 208 Communities using this specification MAY customize error codes if 209 needed. However, in practice, it usually turns out to be of little 210 value compared to a text message and a generic "hard error" code 211 since neither users nor machines can do very much on their own to fix 212 errors that are outside of normal processing. 214 Application level errors are dealt with in an application specific 215 manner. As an example a bank application which finds out that the 216 customer do not have enough funds to perform a transaction would 217 presumably not return an HTTP error code but rather a specifically 218 crafted error message to be displayed to the user. 220 Return codes for successful operation are application specific but 221 are typically 200 (OK) or 201 (Created). 223 4. Processing of JSON Based Requests 225 Assume there is an unsigned HTTP request like the following: 227 POST /foo HTTP/1.1 228 Host: example.com 229 Content-Type: application/json 230 Content-Length: 1234 232 { 233 "something": "data", 235 Additional application specific properties 237 } 239 Adding a signature to the request above would require the following 240 enhancements to the JSON payload: 242 { 243 "something": "data", 245 Additional application specific properties 247 ".secinf": { 248 "uri": "https://example.com/foo", 249 "mtd": "POST", 250 "iat": 1551709923, 251 "jws": "eyJhbGciOiJIUzI1NiJ9..VHVItCBCb849imarDtjw4" 252 } 253 } 255 Notes: 257 o This specification presumes that request data featured in an HTTP 258 body is expressed as a JSON Object. 260 o The "uri" property holds a normalized target URI. 262 o The "mtd" property holds the expected HTTP method. In the example 263 it is actually redundant since the absence of a "mtd" property 264 defaults to "POST" for JSON based requests in this specification. 266 o The "iat" property holds a time stamp in UNIX "epoch" format. 268 o The argument to "jws" (a detached compact JWS) was truncated for 269 brevity. 271 The following subsections detail the operation for requests having an 272 HTTP body. 274 4.1. Request Creation 276 Precondition: the application data to be submitted with the request 277 already exists in a format serializable as a JSON Object, from now on 278 referred to as "message". 280 In order to create a valid signed JSON request, the following steps 281 (and ordering) MUST be adhered to: 283 1. Store the target HTTP method in a variable "targetMethod". 285 2. Store the target URI in a variable "targetURI" after having 286 normalized it as described in Section 6.7. 288 3. Add a JSON Object called ".secinf" to "message". 290 4. Add a property "uri" to ".secinf" where the argument is a JSON 291 String holding a copy of "targetURI". 293 5. Add a property "mtd" to ".secinf" where the argument is a JSON 294 String holding a copy of "targetMethod". 296 Note: for the HTTP method "POST", this step is optional because 297 "POST" is the default for JSON based requests. 299 6. If there is a need to include additional HTTP headers in the 300 signed request data, perform the following steps: 302 * Derive (or define) the hash algorithm to use as described in 303 Section 6.12. Save the algorithm in a variable 304 "hashAlgorithm". 306 * Create a header object as described in Section 6.3. 308 7. Add a time stamp property (see Section 6.11) to ".secinf". 310 Note: if the application data already contains a suitable time 311 stamp property, this step MAY be excluded. 313 8. Create a "JWS Payload" [RFC7515] as described in Section 6.1. 315 9. Create a "JWS Protected Header" [RFC7515] as described in 316 Section 6.4. 318 10. Create a JWS string object as described in Section 6.5. 320 11. Add a property "jws" to ".secinf" where the argument is a JSON 321 String holding the result of the preceding step. 323 12. Serialize "message" into an UTF-8 [UNICODE] encoded byte array 324 called "requestData". 326 13. Submit an HTTP compliant request to the "targetURI" including 327 the following information: 329 * HTTP method set to "targetMethod". 331 * HTTP Header "Content-Length" with the argument set to the 332 length of "requestData". 334 * HTTP Header "Content-Type" with the argument set to 335 "application/json". 337 * All HTTP headers (if any) specified in step 6. 339 * Other HTTP headers (if any) needed by the application. 341 * An HTTP Body containing a copy of "requestData". 343 4.2. Request Validation 345 In order to validate a request the following steps MUST be performed: 347 1. Store the HTTP method of the request in a variable 348 "targetMethod". 350 2. Store the from the request recreated target URI in a variable 351 "targetURI" after having normalized it as described in 352 Section 6.7. 354 3. If the HTTP header "Content-Type" is missing or differs from 355 "application/json" the service MUST reject the request (see 356 Section 3.2). 358 4. If the HTTP header "Content-Length" is missing or malformed the 359 service MUST reject the request (see Section 3.2). Save the 360 length data. 362 5. Read HTTP body data into a byte array with the length retrieved 363 in the preceding step. 365 6. Parse the byte array created in the preceding step with a JSON 366 parser and return the result in an object from now on referred 367 to as "message". If there are parsing errors or if "message" is 368 not a JSON Object the service MUST reject the request (see 369 Section 3.2). 371 7. Using "message", retrieve a JSON Object called ".secinf". If 372 ".secinf" is missing or is not a JSON Object the service MUST 373 reject the request (see Section 3.2). 375 8. Using ".secinf", the property "jws" is read. If "jws" is 376 missing or is not a JSON String the service MUST reject the 377 request (see Section 3.2). Decode the read string as described 378 in Section 6.6. 380 9. Using ".secinf", the property "uri" is read. If "uri" is 381 missing or does not match "targetURI" or is not a JSON String 382 the service MUST reject the request (see Section 3.2). 384 Note: in some proxy arrangements it may be difficult retrieving 385 the proper value of "targetURI". In such cases the comparison 386 with "uri" MAY be disabled. 388 10. Using ".secinf", the property "mtd" is read. if "mtd" is 389 missing the HTTP method is assumed to be "POST" else it is 390 assumed to be the read value. If the derived method does not 391 match "targetMethod" or is not a JSON String the service MUST 392 reject the request (see Section 3.2). 394 11. If the optional "hdr" property is present in ".secinf" perform 395 the following steps: 397 * Derive the hash algorithm to use as described in 398 Section 6.12. Save the algorithm in a variable 399 "hashAlgorithm". 401 * Process the argument of "hdr" as described in Section 6.9. 403 12. Using ".secinf", the property "iat" (see Section 6.11) is read. 404 if "iat" is missing or is not a JSON Number the service MUST 405 reject the request (see Section 3.2). 407 Note: if the application data already contains a suitable time 408 stamp property, this step MAY be excluded. 410 13. Remove the "jws" property from ".secinf". 412 14. Create a "JWS Payload" [RFC7515] as described in Section 6.1. 414 15. Perform signature validation as described in Section 6.10. 416 Note: validation of application specific data can be performed 417 anytime after step 6. The action(s) to perform after a possible 418 failure is out of scope for this specification (see Section 3.2). 420 5. Processing of URI Based Requests 422 Assume there is an unsigned HTTP request like the following: 424 GET /users?id=435 HTTP/1.1 425 Host: example.com 427 The full URI would be as follows: 429 https://example.com/users?id=435 431 Adding a signature to this request according to this specification 432 would return the following URI: 434 https://example.com/users?id=435&.jws=eyJhhiJ.eyJ7fgw.VHVIt 436 Notes: 438 o The revised URI represents a complete serializable signed request 439 object. 441 o The argument to ".jws" (a standard compact JWS) was truncated for 442 brevity. 444 The middle component of the JWS string ("JWS Payload"), contains 445 Base64Url encoded signed data related to the request. It should 446 (after Base64Url decoding) yield a JSON Object like the following: 448 { 449 "htu": "WUjqfXPztLzzXRCs6EcWCw-GC9hSL7hwCR1nG2FSvQ8", 450 "mtd": "GET", 451 "iat": 1551863696 452 } 454 Notes: 456 o The property "htu" holds a Base64Url encoded value of the 457 normalized target URI after it has been hashed by the hash 458 algorithm associated with the JWS signature. 460 o The "mtd" property holds the expected HTTP method. In the example 461 it is actually redundant since the absence of a "mtd" property 462 defaults to "GET" for URI based requests in this specification. 464 o The "iat" property holds a time stamp in UNIX "epoch" format. 466 The following subsections detail the operation for requests using an 467 HTTP query string component for holding a signature. 469 5.1. Request Creation 471 In order to create a valid signed URI request, the following steps 472 (and ordering) MUST be adhered to: 474 1. Store the target HTTP method in a variable "targetMethod". 476 2. Store the target URI in a variable "targetURI" after having 477 normalized it as described in Section 6.7. 479 3. Create an empty JSON Object from now on referred to as 480 ".secinf". 482 4. Derive (or define) the hash algorithm to use as described in 483 Section 6.12. Save the algorithm in a variable "hashAlgorithm". 485 5. Add a property "htu" (Hashed Target URI) to ".secinf" where the 486 argument is a JSON String holding the outcome of the process 487 described in Section 6.2. 489 6. Add a property "mtd" to ".secinf" where the argument is a JSON 490 String holding a copy of "targetMethod". 492 Note: for the HTTP method "GET", this step is optional because 493 "GET" is the default for URI based requests. 495 7. If there is a need to include additional HTTP headers in the 496 signed request data, create a header object as described in 497 Section 6.3. 499 8. Add a time stamp property (see Section 6.11) to ".secinf". 501 9. Serialize the ".secinf" JSON Object into a UTF-8 [UNICODE] byte 502 array representing a "JWS Payload" [RFC7515]. 504 10. Create a "JWS Protected Header" [RFC7515] as described in 505 Section 6.4. 507 11. Create a JWS string object as described in Section 6.5. 509 12. Create a query string component by concatenating ".jws=" with 510 the JWS string created in the preceding step. This component is 511 appended to the original unsigned request URI prepended by & or 512 ? depending on if it is the only query component or not. 514 13. Submit an HTTP compliant request to the target URI including the 515 following information: 517 * HTTP method set to "targetMethod". 519 * All HTTP headers (if any) specified in step 7. 521 * Other HTTP headers (if any) needed by the application. 523 5.2. Request Validation 525 In addition to normal validation of received data (which may be 526 carried out before or after the steps outlined here), the following 527 steps MUST be performed in order to validate a URI based HTTP 528 request: 530 1. Store the HTTP method of the request in a variable 531 "targetMethod". 533 2. Store the from the request recreated target URI in a variable 534 "targetURI" after having normalized it as described in 535 Section 6.7. 537 3. Extract the JWS string from the ".jws" element which MUST reside 538 in the query string of "targetURI". If the JWS string is 539 missing the service MUST reject the request (see Section 3.2). 541 4. Decode the argument of the preceding step as described in 542 Section 6.6. 544 5. Remove the ".jws" query string component from "targetURI". Note 545 that if the ".jws" query component is the last part of 546 "targetURI", the delimiter immediately preceding the ".jws" 547 component is removed, else the succeeding delimiter is removed. 549 6. Parse "JWS Payload" (created in step 4) with a JSON parser and 550 from now on refer to the result as ".secinf". If ".secinf" is 551 not a JSON Object the service MUST reject the request (see 552 Section 3.2). 554 7. Derive the hash algorithm to use as described in Section 6.12. 555 Save the algorithm in a variable "hashAlgorithm". 557 8. Using ".secinf" the property "htu" is read. If "htu" is missing 558 or is not a JSON String the service MUST reject the request (see 559 Section 3.2). 561 9. Perform the operation described in Section 6.2 and compare the 562 outcome with the argument to "htu". If these value do not match 563 the service MUST reject the request (see Section 3.2). 565 Note: in some proxy arrangements it may be difficult retrieving 566 the proper value of "targetURI". In such cases the comparison 567 with "htu" MAY be disabled. 569 10. Using ".secinf", the property "mtd" is read. if "mtd" is 570 missing the HTTP method is assumed to be "GET" else it is 571 assumed to be the read value. If the derived method does not 572 match "targetMethod" or is not a JSON String the service MUST 573 reject the request (see Section 3.2). 575 11. If the optional "hdr" property is present in ".secinf", process 576 the argument of "hdr" as described in Section 6.9. 578 12. Using ".secinf", the property "iat" (see Section 6.11) is read. 579 if "iat" is missing or is not a JSON Number the service MUST 580 reject the request (see Section 3.2). 582 13. Perform signature validation as described in Section 6.10. 584 Note: validation of application specific data can be performed 585 anytime. The action(s) to perform after a possible failure is out of 586 scope for this specification (see Section 3.2). 588 6. Common Operations 590 This specification builds on a modular scheme using common procedures 591 described in the following subsections. 593 6.1. Create Signable JSON Data 595 Unsigned request data is now supposed to reside in "message". To 596 facilitate resilience against (legitimate) variances in JSON 597 processing between different platforms and systems, "message" needs 598 to be canonicalized and serialized into a UTF-8 [UNICODE] encoded 599 byte array. If the used JSON tools offer intrinsic support for JCS 600 [JCS], this is typically a single operation, else the followings 601 steps are performed: 603 1. Serialize "message" using standard JSON tools for the platform. 605 2. Create a canonical and UTF-8 encoded form of the data created in 606 the preceding step, through an external software solution 607 supporting JCS. 609 The output from JCS represents a "JWS Payload" [RFC7515]. 611 6.2. Create Signable URI Data 613 For URI based requests, the steps to create signable URI data are as 614 follows: 616 1. Convert "targetURI" into a UTF-8 [UNICODE] encoded byte array. 618 2. Create a digest of the result of the preceding step using the 619 previously defined "hashAlgorithm". The result is a byte array. 621 3. The result of the preceding step is subsequently Base64Url 622 encoded. 624 The test vectors in Appendix A provide a few examples showing 625 authentic values of the "htu" (Hashed Target URI) property. 627 6.3. Create HTTP Header Object 629 To create a digest of headers to be included in a signed request, 630 perform the following operations: 632 1. Create an empty string "headerBlob". 634 2. Create an empty string "headerList". 636 3. Create a collection of headers to be sent as described in 637 Section 6.8. 639 4. Enumerate the "headerCollection" and perform the following steps 640 for each entry: 642 * Append header field name to "headerList". 644 * Append header field name to "headerBlob". 646 * Append a semicolon (':') to to "headerBlob". 648 * Append header field value to "headerBlob". 650 * For all but the last entry, append a newline (U+000A) to 651 "headerBlob". 653 * For all but the last entry, append a comma (',') to 654 "headerList". 656 5. Create a two element JSON Array object "headerData". 658 6. Run the previously defined "hashAlgorithm" (see Section 6.12) 659 over the UTF-8 [UNICODE] representation of "headerBlob". 661 7. Base64Url-encode the result of the preceding operation and assign 662 the result to the first entry in "headerData" in the form of a 663 JSON String. 665 8. Assign a copy of "headerList" to the second entry in "headerData" 666 in the form of a JSON String. 668 9. Add a property "hdr" to ".secinf" using a copy of "headerData" as 669 argument. 671 Below is an example of header input data: 673 x-debug: full 674 Cache-Control: max-age=60, must-revalidate 676 Applying the process described in this subsection and using the 677 SHA-256 [SHS] hash algorithm should generate the following ".secinf" 678 data: 680 "hdr": ["Ljzuq8C9PScbvLpBxG8GNOs-WQUd7gl7R64izahhe-0", 681 "x-debug,cache-control"] 683 6.4. Create JWS Protected Header 685 Create a "JWS Protected Header" [RFC7515] JSON Object with algorithm 686 and key data adapted for the application. Below is a minimal 687 example: 689 { 690 "alg": "ES256" 691 } 693 6.5. Create JWS String 695 To create a compact JWS object (a string), perform the following 696 steps: 698 1. Serialize the previously defined "JWS Protected Header" object 699 into a UTF-8 [UNICODE] encoded byte array. 701 2. Base64Url-encode the output from the preceding step into a local 702 variable "jwsProtectedHeaderB64U". 704 3. Base64Url-encode the previously defined variable "JWS Payload" 705 into a local variable "jwsPayloadB64U". 707 4. Set a local variable "signedData" to the UTF-8 encoded 708 representation of the concatenation of: 710 * The previously defined variable "jwsProtectedHeaderB64U". 712 * A point character ("."). 714 * The previously defined variable "jwsPayloadB64U". 716 5. Use the designated signature key, signature algorithm and 717 "signedData" to create a "JWS Signature" object (byte array). 719 6. Return the string consisting of the concatenation of: 721 * The previously defined variable "jwsProtectedHeaderB64U". 723 * A period character ('.'). 725 * For URI based requests only: "jwsPayloadB64U". That is, JSON 726 based requests use the detached JWS format described in 727 Appendix F of [RFC7515]. 729 * A period character ('.'). 731 * The previously defined variable "JWS Signature", here encoded 732 in Base64Url [RFC4648]. 734 6.6. Decode JWS String 736 The following processing steps presume that there is an input string 737 holding a JWS compact object, here called "jwsString": 739 1. Verify that "jwsString" has the syntax 741 "header.payload.signature" 743 where the length of the "payload" element is zero for JSON based 744 requests and non-zero for URI based requests. That is, JSON 745 based requests use the detached JWS format described in 746 Appendix F of [RFC7515]. 748 2. Assign the "header" portion of "jwsString" to a variable 749 "jwsProtectedHeaderB64U". 751 3. Base64Url-decode "jwsProtectedHeaderB64U" into a byte array. 753 4. Parse the output from the preceding step with a JSON parser and 754 assume that the result (which MUST be a JSON Object) represents a 755 "JWS Protected Header" [RFC7515]. 757 5. For URI based requests only: 758 base64Url-decode the "payload" portion of "jwsString" into a byte 759 array representing a "JWS Payload" [RFC7515]. 761 6. Base64Url-decode the "signature" portion of "jwsString" into a 762 byte array representing a "JWS Signature" [RFC7515]. 764 If any of the steps above fail, the service MUST reject the request 765 (see Section 3.2). 767 6.7. Normalize Target URI 769 To facilitate comparison between actual (received) URIs and signed 770 URIs, URIs MUST be normalized according to the following: 772 The schema default ports 443 and 80 MUST be removed from HTTPS and 773 HTTP URIs respectively. 775 URI characters that have been escaped that are in the non-reserved 776 set [ALPHA DIGIT '-' '.' '_' '~'] MUST be restored in their 777 natural form. 779 Escape sequences MUST transformed into uppercase. 781 Non-ASCII characters MUST be escaped to their UTF-8 [UNICODE] 782 counterpart. 784 Host names MUST be lowercased. 786 The following URI shows a non-normalized URI: 788 https://EXAMPLE.COM:443/%63EURO%2f 790 Note: EURO denotes a single Euro character (Unicode: U+20AC), 791 which not being ASCII, is currently not displayable in RFCs. 793 The same URI after normalization: 795 https://example.com/c%E2%82%AC%2F 797 [[ This section is still incomplete ]] 799 6.8. Normalize Header Data 801 Headers to be included in signed requests MUST be normalized. This 802 subsection shows a common procedure for senders and receivers based 803 on Section 3.2.4 of [RFC7230]. 805 Collect received headers or headers to be submitted in a list of 806 header field name and header field value pairs according to the 807 following: 809 o Header field names MUST be lowercased. 811 o Leading and trailing optional whitespace (OWS) in the header field 812 value MUST be omitted. If there are multiple instances of the 813 same header field name, all header field values associated with 814 the header field name MUST be concatenated, separated by an ASCII 815 comma and an ASCII space (', '), and used in the order in which 816 they are intended to appear in an HTTP message. Any other 817 modification to the header field value MUST NOT be made. 819 This list is referred to as "headerCollection" in other places in 820 this specification. 822 Below is an example of header input data: 824 x-debug: full 825 Cache-control: max-age=60 826 Cache-Control: must-revalidate 828 Applying the process described in this subsection should generate the 829 following collection: 831 |======================================================| 832 | Header Field Name | Header Field Value | 833 |======================================================| 834 | x-debug | full | 835 |------------------------------------------------------| 836 | cache-control | max-age=60, must-revalidate | 837 |------------------------------------------------------| 839 For interoperability reasons it is RECOMMENDED to not use duplicate 840 header names for headers that are to be signed. Apparently proxy 841 servers do not always honor original header ordering. 843 6.9. Validate HTTP Header Object 845 To validate a digest of headers in a signed request, perform the 846 following operations: 848 1. Create a collection of received headers as described in 849 Section 6.8. 851 2. Create an empty string "headerBlob". 853 3. Read the "hdr" property of ".secinf". This MUST be a JSON Array 854 holding exactly two JSON String elements. 856 4. Perform the following actions on the data obtained in the 857 preceding step: 859 * Base64Url-decode the first string into a byte array "digest". 861 * Split the second string into ordered array of strings called 862 "headerList". Note that the format MUST be a list of header 863 field names in lowercase, separated by comma (',') characters. 864 There MUST NOT be any whitespace or terminating comma in this 865 string. 867 * Verify that the received "headerList" contains the header 868 field names as defined by an application specific policy. 870 5. Enumerate the "headerList" and perform the following steps for 871 each entry: 873 * Append header field name to "headerBlob". 875 * Append a semicolon (':') to to "headerBlob". 877 * Retrieve the matching header field value from 878 "headerCollection". 880 * Append the result of the preceding step to "headerBlob". 882 * For all but the last entry, append a newline (U+000A) to 883 "headerBlob". 885 6. Run the previously defined "hashAlgorithm" (see Section 6.12) 886 over the UTF-8 [UNICODE] representation of "headerBlob". 888 7. Verify that the result of the preceding step is identical to 889 "digest". 891 If any of the steps above fail, the service MUST reject the request 892 (see Section 3.2). 894 Note that this specification does not enforce any particular ordering 895 of signed header elements. 897 6.10. Validate JWS Signature 899 Validation of the JWS [RFC7515] object, using the previously 900 extracted and decoded objects requires the following steps: 902 1. Verify that the received "JWS Protected Header" contains a JWS 903 algorithm ("alg") and key identifiers that matches the needs of 904 the application. 906 2. Retrieve the signature validation key. This part is application 907 specific since the key may be implicit, specified by a key ID 908 ("kid") or be supplied in a certificate path ("x5c"). 910 3. Set a local variable "signedData" to the UTF-8 [UNICODE] encoded 911 representation of the string created by concatenating the 912 following elements: 914 * The previously collected variable "jwsProtectedHeaderB64U". 916 * A period character ('.'). 918 * The previously collected variable "JWS Payload", but here 919 encoded in Base64Url [RFC4648]. 921 4. Validate the signature using the algorithm retrieved in step 1, 922 the signature validation key from step 2, "signedData" from step 923 3 and the previously collected "JWS Signature" object (byte 924 array). 926 If any of the steps above fail, the service MUST reject the request 927 (see Section 3.2). 929 6.11. Time Stamps 931 Time stamps have the same name ("iat"), format and function as 932 described in JWT [RFC7519], Section 4.1.6. However, in this 933 specification time stamps are REQUIRED, and stored in the ".secinf" 934 JSON Object. 936 Although JWT permits non-integer values, implementers of this 937 specification SHOULD limit generated time stamp granularity to 938 seconds and use integer representation. 940 The policy with respect to the difference between the current time 941 and received time stamps is out of scope for this specification. 942 However, for security reasons it is generally a good idea limiting 943 deviations to a few minutes as well as using network based clock 944 synchronization in both ends. 946 6.12. Hash Algorithms 948 Inclusion of HTTP header elements as well as the "htu" property of 949 URI based requests depends on digests produced by a hash algorithm. 950 The default is using the hash algorithm associated with the JWS 951 signature algorithm ("alg") featured in the "JWS Protected Header". 952 That is, the JWA [RFC7518] algorithms "ES256" and "HS384" imply the 953 hash algorithms SHA-256 and SHA-384 respectively. 955 In case this is not desired, this specification permits overriding 956 the default by including a "hao" (Hash Algorithm Override) property 957 in the ".secinf" JSON Object. The currently recognized arguments to 958 "hao" are: 960 "S256" for SHA-256 [SHS] 962 "S384" for SHA-384 [SHS] 963 "S512" for SHA-512 [SHS] 965 7. Local Naming Conventions 967 Although using the ".secinf" JSON property name and ".jws" query 968 component name is RECOMMENDED, this specification permits (=being 969 considered as compatible), the use of local naming conventions as 970 long as the specified procedures and formats are adhered to. 972 Local naming conventions MUST be properly communicated in the 973 community using them. 975 8. Attachments 977 [[ 979 It may be possible to extending the JSON based request to also 980 support attachments using MIME multipart schemes. This is though 981 currently out of scope for this specification. 983 An alternative to attachments is featuring such data in Base64Url 984 encoded fields. 986 Recently, "cloud" based schemes using (preferably time-limited) 987 URLs with hard-to-guess nonce values have become a viable method 988 for supporting related additional data. By combining hash values 989 with such URLs, integrity of the additional data can be verified. 991 ]] 993 9. IANA Considerations 995 This document currently has no IANA actions but the reserved names 996 below could be candidates for IANA registration: 998 .secinf 999 JSON Object holding the security related data of this 1000 specification. 1002 .jws 1003 HTTP query component holding the security related data of this 1004 specification. 1006 The hash algorithms defined in Section 6.12 could also benefit from 1007 IANA registration. 1009 10. Security Considerations 1011 The purpose of this specification is adding an integrity and 1012 authorization layer to HTTP requests. This part is subject to the 1013 same security considerations as the underpinning JCS and JWS schemes. 1015 For most applications HTTPS [RFC7231] would be the logical choice, 1016 not only for protecting application data from snooping, but also to 1017 not unnecessary reveal data about signature keys. 1019 In a cloud scenario with Web servers open for access by any party new 1020 security challenges are introduced. Cryptographic solutions protect 1021 data but may also add vulnerabilities to denial-of-service attacks 1022 since they often need substantial processing. 1024 Protecting against replay attacks is important because replay may 1025 actually be a legitimate facility for systems repeating a request due 1026 to a communication failure. This cannot be entirely solved by time 1027 stamps and cryptography; you usually need unique transactions IDs and 1028 data base support as well. For reliable operation there must be 1029 common rules within a community using such features. The REST [REST] 1030 paradigm also requires such measures due to the idempotent operation 1031 specified for "PUT", "GET" and "DELETE. 1033 11. Acknowledgements 1035 Parts of this specification were derived from the HTTP signature 1036 [HTTPSIG] draft. 1038 12. References 1040 12.1. Normative References 1042 [JCS] A. Rundgren, B. Jordan, S. Erdtman, "JSON Canonicalization 1043 Scheme - Work in progress", . 1046 [JWSJCS] A. Rundgren, "Combined JWS and JCS Signature Scheme - Work 1047 in progress", . 1049 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 1050 Requirement Levels", BCP 14, RFC 2119, 1051 DOI 10.17487/RFC2119, March 1997, 1052 . 1054 [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 1055 Resource Identifier (URI): Generic Syntax", STD 66, 1056 RFC 3986, DOI 10.17487/RFC3986, January 2005, 1057 . 1059 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 1060 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 1061 . 1063 [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 1064 Protocol (HTTP/1.1): Message Syntax and Routing", 1065 RFC 7230, DOI 10.17487/RFC7230, June 2014, 1066 . 1068 [RFC7231] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 1069 Protocol (HTTP/1.1): Semantics and Content", RFC 7231, 1070 DOI 10.17487/RFC7231, June 2014, 1071 . 1073 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 1074 DOI 10.17487/RFC7493, March 2015, 1075 . 1077 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 1078 Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 1079 2015, . 1081 [RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, 1082 DOI 10.17487/RFC7518, May 2015, 1083 . 1085 [RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token 1086 (JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015, 1087 . 1089 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 1090 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 1091 May 2017, . 1093 [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 1094 Interchange Format", STD 90, RFC 8259, 1095 DOI 10.17487/RFC8259, December 2017, 1096 . 1098 [SHS] National Institute of Standards and Technology, "Secure 1099 Hash Standard (SHS)", FIPS PUB 180-4, August 2015, 1100 . 1103 [UNICODE] The Unicode Consortium, "The Unicode Standard, Version 1104 10.0.0", 1105 . 1107 12.2. Informal References 1109 [AWS] Amazon.com, "Signing AWS API Requests", 1110 . 1113 [FAPI] Open ID, "Financial-grade API", 1114 . 1116 [HTTPSIG] M. Cavage, M. Sporny, "Signing HTTP Messages", 1117 . 1120 [OBIE] Open Banking UK, "Open Banking API", 1121 . 1123 [REST] Roy Fielding, "Architectural Styles and the Design of 1124 Network-based Software Architectures", 1125 . 1127 [STET] STET, "PSD2 API V1.4.1", . 1129 12.3. URIs 1131 [1] https://github.com/cyberphone/ietf-signed-http-requests 1133 Appendix A. Test Vectors 1135 The following test vectors "activate" all parts of the specification. 1136 After removing the line breaks needed for publishing, the test 1137 vectors are supposed to be fully validatable. 1139 A.1. Type=URI, Method=GET, Algorithm=HS256 1141 Target URI: 1143 https://example.com/users/456 1145 Signed URI: 1147 https://example.com/users/456?.jws=eyJhbGciOiJIUzI1NiJ9.eyJodHUi 1148 OiJmaVZpNGpZaER0N1ZDdVFJS1VJZFdJTkVXZm9oX05YSGZMVFpORWVTYXZZIiwi 1149 aWF0IjoxNTUxOTUxOTAwfQ.Wll5cFEE9sidHs01sADus8kbHNHAC5DCzyytYoAtT 1150 2g 1152 Decoded JWS Payload: 1154 { 1155 "htu": "fiVi4jYhDt7VCuQIKUIdWINEWfoh_NXHfLTZNEeSavY", 1156 "iat": 1551951900 1157 } 1159 Symmetric signature validation key, here in hexadecimal notation: 1161 7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a 1163 A.2. Type=JSON, Method=POST, Algorithm=ES256 1165 Target URI: 1167 https://example.com/users 1169 JSON Body: 1171 { 1172 "name": "John Doe", 1173 "profession": "Unknown", 1174 ".secinf": { 1175 "uri": "https://example.com/users", 1176 "iat": 1551951900, 1177 "jws": "eyJhbGciOiJFUzI1NiJ9..-N7yuF1TEASo5Ub5q2T1_EkLWrWHs2 1178 nyHjDupkinoRcQbSo8h2ygL9pmGzd_YU4jn_bcMQF8BrTIlSioNel5GQ" 1179 } 1180 } 1182 Public signature validation key, here in PEM format: 1184 -----BEGIN PUBLIC KEY----- 1185 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcensDzcMEkgiePz6DXB7cDuwFems 1186 hAFR90UNVQFCg8TGryvN7p7AbT55VxIXvYnvuAqIPQgefOnAdpTu3qdV5g== 1187 -----END PUBLIC KEY----- 1189 A.3. Type=JSON, Method=PUT, Algorithm=ES256 1191 Target URI: 1193 https://example.com/users/456 1195 JSON Body: 1197 { 1198 "name": "Jane Smith", 1199 "profession": "Hacker", 1200 ".secinf": { 1201 "uri": "https://example.com/users/456", 1202 "mtd": "PUT", 1203 "iat": 1551951900, 1204 "jws": "eyJhbGciOiJFUzI1NiJ9.._VWTXYcgr6OTCcJg6XZzPkHsLU-jUT 1205 T1HoQ92bihMIDlXR7xNfmxlHWSUc9cyFCxzsBy9yq33eFn3fApIH42SA" 1206 } 1207 } 1209 Public signature validation key, here in PEM format: 1211 -----BEGIN PUBLIC KEY----- 1212 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcensDzcMEkgiePz6DXB7cDuwFems 1213 hAFR90UNVQFCg8TGryvN7p7AbT55VxIXvYnvuAqIPQgefOnAdpTu3qdV5g== 1214 -----END PUBLIC KEY----- 1216 A.4. Type=URI, Method=DELETE, Algorithm=RS256 1218 Target URI: 1220 https://example.com/users/456 1222 Signed URI: 1224 https://example.com/users/456?.jws=eyJhbGciOiJSUzI1NiJ9.eyJodHUi 1225 OiI5R3FtRDBSRWRqSDFZNklvSXR3UjdKRURuU0pjVzNuSnhoM085eHQ3Zk1RQ1cy 1226 N3FtOEQyWUNtN1h4RzRwU1hwOGJFM3lTT3RzWlhIR0VJSWw1M05jQSIsIm10ZCI6 1227 IkRFTEVURSIsImlhdCI6MTU1MTk1MTkwMCwiaGFvIjoiUzUxMiIsImhkciI6WyIz 1228 ZXBrQno4RUJwMUxYX01EdFd1WnFWZjFLYjJyalFNZzE5RjVvT2Fhbk91SVFpS1Z1 1229 SHBrSG5WdWFLMlZZbVZ2bEpOSGxEY2NEeHFxVGQxNFU5VXg5USIsIngtZGVidWci 1230 XX0.YRTEbCrOy11c10HcPSDX_DCtl56S5qmcYWFcuG6wqsgg7vnCr22vCDhE1xJL 1231 xeM2hp_-gSmdxykJ-Q060xetax-nMmXUhrDtcRoeCfO12-xDTymZTJXtb11SX6Pn 1232 mt9CnM4ZOVrJVro7eLW8hCc4p5As7zDS2arNM_-IsWiNJ1T25EDb8ZS7kLLSA6Im 1233 lo31o8815kC0oHNI0q-lZeaOX3DhnL1tMJKZQzrItXvmZ0oqJ3kL8bxF6aglOFC0 1234 zOYUU2kciIf55jVcfBgwupecFw-rN56QEg8PzA8YA-nGPWHBpxJUWWseY4qXZudR 1235 cQQZtms7Yc1yK7z3fNhht6Oh1A 1237 Decoded JWS Payload: 1239 { 1240 "htu": "9GqmD0REdjH1Y6IoItwR7JEDnSJcW3nJxh3O9xt7fMQCW27qm8D2YC 1241 m7XxG4pSXp8bE3ySOtsZXHGEIIl53NcA", 1242 "mtd": "DELETE", 1243 "iat": 1551951900, 1244 "hao": "S512", 1245 "hdr": ["3epkBz8EBp1LX_MDtWuZqVf1Kb2rjQMg19F5oOaanOuIQiKVuHpkH 1246 nVuaK2VYmVvlJNHlDccDxqqTd14U9Ux9Q", "x-debug"] 1247 } 1249 Note the overridden hash algorithm. 1251 Required HTTP Headers: 1253 x-debug: full 1255 Public signature validation key, here in PEM format: 1257 -----BEGIN PUBLIC KEY----- 1258 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhFWEXArvaZEpSP5qNX7x 1259 4C4Hl28GJQTNvnDwkfqiWs63kXbdyPeS06bz6GnY3tfQ/093nGauWsimqKBmGAGM 1260 PtsV83Qxw1OIeO4ujbIIb9pema0qtVqs0MWlHxklZGFkYfAmbuEUFxYDeLDHe0bk 1261 kXbSlB7/t8pCSvc8HLgHjEQjYOlFRwjR0D+uLo+xgsCbpmCtYkB5lcT/zFgpRgY4 1262 zJNLSv7GZiz2S4Fc5ArGjd34lL47+L8bozuYjqNOv9sqX0Zgll5XaJ1ndvr7UqZu 1263 1xQFgm38reoM3IarBP/SkEFbt/v9iak602VO3k28fQhMaocP7JWR2YLT3kZM0+WT 1264 FwIDAQAB 1265 -----END PUBLIC KEY----- 1267 Appendix B. Other Signed HTTP Request Solutions 1269 This appendix briefly outlines a few other solutions addressing 1270 Signed HTTP Requests. 1272 B.1. Amazon Web Services 1274 AWS provides a system for their clients using HTTP headers holding 1275 security constructs while the digested HTTP body may hold any valid 1276 media type. For more information see the [AWS]. Signatures may also 1277 be added to query strings in a similar fashion to Section 5. 1279 B.2. HTTP Signatures 1281 HTTP Signatures is a system using HTTP headers holding security 1282 constructs while the (optional) digested HTTP body may hold any valid 1283 media type. This scheme has been adopted by the French Open Banking 1284 API [STET]. For more information see the Internet draft [HTTPSIG]. 1285 HTTP Signatures also supports signed response data. 1287 B.3. Open Banking (UK) 1289 The current (3.1) version of the Open Banking API [OBIE] use a scheme 1290 where a dedicated HTTP header holds a detached JWS signature covering 1291 a clear text JSON message in the HTTP body: 1293 POST /foo HTTP/1.1 1294 Host: example.com 1295 Content-Type: application/json 1296 x-jws-signature: eyJhbGciOiJSUzI1N..SD7xMbpL-2QgwUsAlMGzw 1297 Content-Length: 2765 1299 { 1300 "something": "data", 1302 Additional application specific properties 1304 } 1306 Notes: 1308 o The HTTP URI, method and headers are unsigned. 1310 o The signature argument (a JWS) was truncated for brevity. 1312 B.4. Financial API 1314 The current version (Draft 06) of the financial API [FAPI] use a 1315 scheme where the payload is signed using JWS in Base64Url mode: 1317 POST /foo HTTP/1.1 1318 Host: example.com 1319 Content-Type: application/jws 1320 Content-Length: 1288 1322 eyJhbGcRjIn0.ew0KICJfds56gty5ypc3MiOiA.2QgwUsA565656lMGzw 1324 Notes: 1326 o The HTTP URI, method and headers are unsigned. 1328 o The JWS signature was truncated for brevity. 1330 Appendix C. Development Portal 1332 The SHREQ specification is currently developed at: 1333 https://github.com/cyberphone/ietf-signed-http-requests [1]. 1335 Author's Address 1337 Anders Rundgren 1338 Independent 1339 Montpellier 1340 France 1342 Email: anders.rundgren.net@gmail.com 1343 URI: https://www.linkedin.com/in/andersrundgren/