I don't understand why JWS unprotected headers exist.
For some context: a JWS unprotected header contains parameters that are not integrity protected and can only be used per-signature with JSON Serialization.
If they could be used as a top-level header, I could see why someone could want to include a mutable parameter (that wouldn't change the signature). However, this is not the case.
Can anyone think of a use-case or know why they are included in the spec?
Thanks!
JWS Spec
The answer by Florent leaves me unsatisfied.
Regarding the example of using a JWT to sign a hash of a document... the assertion is that the algorithm and keyID would be "sensitive data" that needs to be "protected". By which I suppose he means "signed". But there's no need to sign the algorithm and keyID.
Example
Suppose Bob creates a signed JWT, that contains an unprotected header asserting alg=HS256 and keyid=XXXX1 . This JWT is intended for transmission to Alice.
Case 1
Suppose Mallory intercepts the signed JWT sent by Bob. Mallory then creates a new unprotected header, asserting alg=None.
The receiver (Alice) is now responsible for verifying the signature on the payload. Alice must not be satisfied with "no signature"; in fact Alice must not rely on a client (sender) assertion to determine which signing algorithm is acceptable for her. Therefore Alice rejects the JWT with the contrived "no signature" header.
Case 2
Suppose Mallory contrives a header with alg=RS256 and keyId=XXX1. Now Alice tries to validate the signature and finds either:
the algorithm is not compliant
the key specified for that algorithm does not exist
Therefore Alice rejects the JWT.
Case 3
Suppose Mallory contrives a header with alg=HS256 and keyId=ZZ3. Now Alice tries to validate the signature and finds the key is unknown, and rejects the JWT.
In no case does the algorithm need to be part of the signed material. There is no scenario under which an unprotected header leads to a vulnerability or violation of integrity.
Getting Back to the Original Question
The original question was: What is the purpose of an unprotected JWT header?
Succinctly, the purpose of an unprotected JWS header is to allow transport of some metadata that can be used as hints to the receiver. Like alg (Algorithm) and kid (Key ID). Florent suggests that stuffing data into an unprotected header could lead to efficiency. This isn't a good reason. Here is the key point: The claims in the unprotected header are hints, not to be relied upon or trusted.
A more interesting question is: What is the purpose of a protected JWS header? Why have a provision that signs both the "header" and the "payload"? In the case of a JWS Protected Header, the header and payload are concatenated and the result is signed. Assuming the header is JSON and the payload is JSON, at this point there is no semantic distinction between the header and payload. So why have the provision to sign the header at all?
One could just rely on JWS with unprotected headers. If there is a need for integrity-protected claims, put them in the payload. If there is a need for hints, put them in the unprotected header. Sign the payload and not the header. Simple.
This works, and is valid. But it presumes that the payload is JSON. This is true with JWT, but not true with all JWS. RFC 7515, which defines JWS, does not require the signed payload to be JSON. Imagine the payload is a digital image of a medical scan. It's not JSON. One cannot simply "attach claims" to that. Therefore JWS allows a protected header, such that the (non JSON) payload AND arbitrary claims can be signed and integrity checked.
In the case where the payload is non-JSON and the header is protected, there is no facility to include "extra non signed headers" into the JWS. If there is a need for sending some data that needs to be integrity checked and some that are simply "hints", there really is only one container: the protected header. And the hints get signed along with the real claims.
One could avoid the need for this protected-header trick, by just wrapping a JSON hash around the data-to-be-signed. For example:
{
"image" : "qw93u9839839...base64-encoded image data..."
}
And after doing so, one could add claims to this JSON wrapper.
{
"image" : "qw93u9839839...base64-encoded image data..."
"author" : "Whatever"
}
And those claims would then be signed and integrity-proected.
But in the case of binary data, encoding it to a string to allow encapsulation into a JSON may inflate the data significantly. A JWS with a non-JSON payload avoids this.
HTH
The RFC gives us examples of unprotected headers as follows:
A.6.2. JWS Per-Signature Unprotected Headers
Key ID values are supplied for both keys using per-signature Header Parameters. The two JWS Unprotected Header values used to represent these key IDs are:
{"kid":"2010-12-29"}
and
{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}
https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.6.2
The use of kid in the example is likely not coincidence. Because JWS allows multiple signatures per payload, a cleartext hint system could be useful. For example, if a key is not available to the verifier (server), then you can skip decoding the protected header. The term "hint" is actually used in the kid definition:
4.1.4. "kid" (Key ID) Header Parameter
The "kid" (key ID) Header Parameter is a hint indicating which key was used to secure the JWS. This parameter allows originators to explicitly signal a change of key to recipients. The structure of the "kid" value is unspecified. Its value MUST be a case-sensitive string. Use of this Header Parameter is OPTIONAL.
https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.4
If we look at Key Identification it mentions where you a kid does not have to be integrity protected (ie: part of unprotected headers): (emphasis mine)
6. Key Identification
It is necessary for the recipient of a JWS to be able to determine the key that was employed for the digital signature or MAC operation. The key employed can be identified using the Header Parameter methods described in Section 4.1 or can be identified using methods that are outside the scope of this specification. Specifically, the Header Parameters "jku", "jwk", "kid", "x5u", "x5c", "x5t", and "x5t#S256" can be used to identify the key used. These Header Parameters MUST be integrity protected if the information that they convey is to be utilized in a trust decision; however, if the only information used in the trust decision is a key, these parameters need not be integrity protected, since changing them in a way that causes a different key to be used will cause the validation to fail.
The producer SHOULD include sufficient information in the Header Parameters to identify the key used, unless the application uses another means or convention to determine the key used. Validation of the signature or MAC fails when the algorithm used requires a key (which is true of all algorithms except for "none") and the key used cannot be determined.
The means of exchanging any shared symmetric keys used is outside the scope of this specification.
https://datatracker.ietf.org/doc/html/rfc7515#section-6
Simplified, if you have a message that by somebody modifying the kid will refer to another key, then the signature itself will not match. Therefore you don't have to include the kid in the protected header. A good example of the first part, where the information they convey is to be utilized in a trust decision, is the ACME (aka the Let's Encrypt protocol). When creating an account, and storing the key data, you want to trust the kid. We want to store kid, so we need to make sure it's valid. After the server has stored the kid and can use it to get a key, we can push messages and reference the kid in unprotected header (not done by ACME but possible). Since we're only going to verify the signature, then the kid is used a hint or reference to which kid was used for the account. If that field is tampered with, then it'll point to a nonexistent of completely different key and fail the signature the check. That means the kid itself is "the only information used in the trust decision".
There's also more theoretical scenarios that, knowing how it works you can come up with.
For example: the idea of having multiple signatures that you can pass on (exchange). A signing authority can include one signature that can be for an intermediary (server) and another for the another recipient (end-user client). This is differentiated by the kid and the server doesn't need to verify or even decode the protected header or signature. Or perhaps, the intermediary doesn't have the client's secret in order to verify a signature.
For example, a multi-recipient message (eg: chat room) could be processed by a relay/proxy and using kid in the unprotected header, pass along a reconstructed compact JWS (${protected}.${payload}.${signature}) for each recipient based on kid (or any other custom unprotected header field, like userId or endpoint).
Another example, would be a server with access to many different keys and a cleartext kid would be faster than iterating and decoded each protected field to find which one.
From one perspective, all you're doing is skipping base64url decoding the protected header for performance, but if you're going to proxy/relay the data, then you're not polluting the protected header which is meant for another recipient.
Related
I have to create a JWS signature and the JOSE header has to look like this :
{
"alg": "HS256",
"kid": "V3vEe66RJm85eD72",
"b64": false,
"http://openbanking.org.uk/iat": 1501497671,
"http://openbanking.org.uk/iss": "C=UK, ST=England, L=London, O=Acme Ltd.",
"crit": ["b64","http://openbanking.org.uk/iat","http://openbanking.org.uk/iss"]
}
Where do I have to put this header in jwt.io website or does someone know other good sites for creating jws signature?
The problem is, that when I change the header which is by default there in jwt.io with the header type provided by the documentation it says in jwt.io at the bottom "Invalid Signature" , why ?
You can add the header into the "HEADER" section in the right column of the jwt.io debugger.
Then you add a secret in the field under "VERIFY SIGNATURE" and get a token.
Your JOSE Header contains a crit claim, which leads to a "Invalid Signature":
The "crit" (critical) Header Parameter indicates that extensions to
this specification and/or [JWA] are being used that MUST be
understood and processed. Its value is an array listing the Header
Parameter names present in the JOSE Header that use those extensions.
If any of the listed extension Header Parameters are not understood
and supported by the recipient, then the JWS is invalid.
The signature itself is fine, it's just the critclaim, that causes an invalid signature error. As soon as you have a crit claim with a non empty list, the verification fails on jwt.io.
You can verify the resulting token
eyJhbGciOiJIUzI1NiIsImtpZCI6IlYzdkVlNjZSSm04NWVENzIiLCJiNjQiOmZhbHNlLCJodHRwOi8vb3BlbmJhbmtpbmcub3JnLnVrL2lhdCI6MTUwMTQ5NzY3MSwiaHR0cDovL29wZW5iYW5raW5nLm9yZy51ay9pc3MiOiJDPVVLLCBTVD1FbmdsYW5kLCBMPUxvbmRvbiwgTz1BY21lIEx0ZC4iLCJjcml0IjpbImI2NCIsImh0dHA6Ly9vcGVuYmFua2luZy5vcmcudWsvaWF0IiwiaHR0cDovL29wZW5iYW5raW5nLm9yZy51ay9pc3MiXX0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.QrI016I1j2kKE-cth3xr8O5DUOLOrL-biUfkuVZb_Xo
(created with the secret "secret")
on https://www.jsonwebtoken.io/ and see that it can be verified. This website seems not to care about the crit header and checks only based on the hashing.
(note: this website doesn't show the correct header and payload of your token after decoding)
Generally you should not take these online tools too serious. They're meant for testing and educational purposes, but not as a production tool.
Kindly clarify on the following queries about the OPC UA specification Part 4 Services,
From my understanding after the GetEndpoints Service messages, the client sends a OpenSecureChannel request to the server which means the request is signed or signed & encrypted according the security policy in the desired endpoint. I saw the following lines in the specification Part 4 Services, pg.no 33,
The OpenSecureChannel request and response Messages shall be signed
with the sender’s Certificate. These Messages shall always be
encrypted. If the transport layer does not provide encryption, then
these Messages shall be encrypted with the receiver’s Certificate.
In this it is mentioned that the messages shall always be encrypted. How does it rely on transport layer ?
And I also see another description in the OPC Unified Architecture Book by Wolfgang Mahnke, Stefan-Helmut Leitner, Matthias Damm as follows,
If the certificate is considered as trustworthy, then as the second
step an OpenSecureChannel request secured in accordance to the
Security Policy and the Security Mode is sent to the selected Session
Endpoint of the server. (Chapter 7, pg.no:213)
Here, it highlights that the message is secured in accordance to the Security Policy and Security Mode, so I request a clarification on the scenario if the security mode is sign?
Will the message be encrypted also?
In general, when the MessageSecurityMode is None then the SecurityPolicy ‘must’ be None? What is the exact usage of 'Invalid' MessageSecurityMode?
X509IdentityToken specification Part 4 Services section 7.35.4
This token shall always be accompanied by a signature in the
userTokenSignature parameter of ActivateSession if required by the
SecurityPolicy. The Server should specify a SecurityPolicy for the
UserTokenPolicy if the SecureChannel has a SecurityPolicy of None.
In the first line it is mentioned that ‘if required by the SecurityPolicy’, what is context of ‘if required’ when the userTokenSignature is a required field for X509IdentityToken ? In the second line it is mentioned that a explicit Security Policy is required if the SecureChannel has a SecurityPolicy of None, where ‘None’ means no certificates are exchanged and so X509IdentityToken cannot be used, referring to the following lines in the same specification (Section 5.6.3 ActivateSession, pg.no: 40)?
If the token is an X509IdentityToken then the proof is a signature
generated with private key associated with the Certificate. The data
to sign is created by appending the last serverNonce to the
serverCertificate specified in the CreateSession response.
For your kind information I use the specification released on November 2015 to study. Please clarify.
For the first question, the specification continues with "These requirements for OpenSecureChannel only apply if the securityPolicyUri is not None". So the answer is that a SecureChannel will both sign and encrypt the OpenSecureChannelRequest/Response for any MessageSecurityMode, except MessageSecurityMode.None.
For the second question, if MessageSecurityMode is None, then the Security policy is none. There is no usage of MessageSecurityMode.Invalid as far as I can find.
For the third question, if the securityPolicyUri is None, then no application certificates are exchanged. The client can provide a UserIdentity by a X509IdentityToken. The token consists of a X509Certificate and a signature. The signature is generated by appending the server nonce to the server certificate and signing with the user certificate's private key.
I have problems verifying a SAML-response I get form an ADFS Server. I get the response as an url like CALLBACK_URL?SAMLResponse=ENCODED_XML&Signature=SIGNATURE_VALUE&SigAlg=SIGNATURE_ALGORITHMwhile SIGNATURE_ALGORITHM is http://www.w3.org/2001/04/xmldsig-more#rsa-sha256. I managed to decode the response but I am not able to find a way to verify the response using the given signature.
My main problem is that the signature has a very unexpected format. Because of the given signature algorithm I expect the signature to have a length of 32 bytes but what I get when I base64-decode the signature is a string with length 256.
I expect that decoding the signature using base64 is not sufficient. Unfortunately I have not been able to find out what to do next so far. So my question is: What do I have to do to correctly decode the signature to be able to verify it?
I believe you're conflating a hash with a signature.
The signature takes the hash of the message as input and encrypts it with a key. In your case the SigAlg parameter indicates that the 32 byte hash of the SAML message is encrypted with (apparently) a 2048 bits RSA private key, possibly using PKCS#1 v1.5 padding as described in https://www.rfc-editor.org/rfc/rfc6931#section-2.3.2 resulting in a 256 byte signature that can be verified with the associated 2048 bits RSA public key of the sender.
Apart from that: I assume your referring to a SAML request rather than a SAML response since the latter must not be sent by using the HTTP redirect binding as written down in the Web Browser SSO Profile specification, page 16: https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf (top of the page):
Identity Provider issues to Service Provider In step 5, the identity provider issues a message to be delivered by
the user agent to the service provider. Either the HTTP POST, or HTTP
Artifact binding can be used to transfer the message to the service
provider through the user agent. The message may indicate an error, or
will include (at least) an authentication assertion. The HTTP Redirect
binding MUST NOT be used, as the response will typically exceed the
URL length permitted by most user agents.
I am trying to Use SOAPUI to generate the SOAP request which uses WS-Security configuration.
The request requires me to sign the
_ Body
- TimeStamp
- and Binary Security Token
I'm able to do the body and timestamp part but if I specify "name" as Binary Security token in part portion of generating Signature it gives me error.
Does any body has ever encountered this issue in SOAP-UI?
in your outgoing ws-security configuration you have to add three elements
Timestamp
Signature, in the first signature, you have to choose for "key identifier type" BinarySecuritytoken
Signature, in the second signature, you have to choose for "key identifier type" Issuer Name and Serial Number.
Then you have to add the signed parts to sign into parts section.
Each element to sign, you have to note the oder to sign. In my case was Timestamp, Body and then BinarySecurityToken.
Fill name space with the above elements (Timestamp, Body...), fill the namespace and you have to choose element into Encode section.
Then you have to apply the outogoing security to your request.
I hope to help you.
Is there any way to verify the OpenSSL signature using only {signature,hashed message} pair, skipping the original file to be presented for verification?
I need to verify the signature with only {signature,hashed message} pair remotely so using the original file is cumbersome specially when its very large.
Is there any way to verify the OpenSSL signature using only hash value and without needing the original file?
Yes, but there are strings attached.
The scheme which requires the original message to be presented to the verifying function is a Signature Scheme with Appendix (SSA). A scheme like the old PKCS #1.0 signing is an example of it.
The scheme which does not require the original message is a Signature Scheme with Recovery (PSSR). In a PSSR, the encoded message is part of the signature and masked. A scheme like the new PKCS #2.0 PSSR signing is an example of it.
There are no schemes that take just a hash, as far as I know. You have to have the {message,signature} pair. Allowing the message to be disgorged from the signing or verification can be a security violation.
OpenSSL provides both of them, as does most other security libraries, like Botan, Crypto++, NSS, etc.
Also see RSA signature on TLS on Information Security Stack Exchange.
I have been trying to verify the signature with hash value remotely so using the original file is cumbersome specially when its very large.
That's the insecure thing signature schemes want to avoid....