I am trying to verify a saml response signature using the X509 certificate (stored in a .cer file), as part of a login process in Azure's AD SSO.
For some reason, calling :public_key.verify(message, :sha256, signature, public_key) keeps returning false.
I might have misunderstood what should be the values of the parameters sent to the verify function,
here are the values I used:
public_key:
{:ok, cert} = File.read(“test-saml.cer”)
certificate = X509.Certificate.from_pem(cert)
public_key = X509.Certificate.public_key(certificate)
signature:
tried both - sending the raw value from the SignatureValue element in the saml
and decoding it as shown here
raw_signature = Base.decode64!(encoded_signature)
size = div(byte_size(raw_signature), 2)
<<r::binary-size(size), s::binary-size(size)>> = raw_signature
signature = <<48, 129, 136, 2, size, r::binary, 2, size, s::binary>>
messgae:
for message I sent the value of the DigestValue element in the saml.
Are these the expected parameters?
Related
I need to be able to verify the signature of a webhook but I cannot seem to match the value correctly. The tool I'm using provides the expected signature as a URL param with the request:
YOUR_CALLBACK_URL?signature=ofdiwefjojiefwojowefoi
# www.websitename.com?signature=ofdiwefjojiefwojowefoi
They state that the way they generate the signature is:
The signature is generated using an HMAC-SHA-256 base64 digest of the raw HTTP Body of the Webhook post using this Webhook secret.
You can generate the signature in php as follows:
$request_body = file_get_contents('php://input');
$s = hash_hmac('sha256', $request_body, 'mySecret', true);
echo base64_encode($s);
In my app, I attempt to generate a matching signature by doing the following:
key = ENV['ESIGNGENIE_SECRET']
data = params.to_json
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)).strip()
return signature == params["signature"]
This seems to always be wrong. Am I doing something wrong here? I can't tell if I'm encountering issues due to the way Rails parses the json object or what.
After doing some research I realized that my mistake was trying to use params to generate the signature when I should have used request.body.read. It should look like the following:
key = ENV['ESIGNGENIE_SECRET']
data = request.body.read
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)).strip()
return signature == params["signature"]
I am trying to secure my web api (.net core 2.2) with Azure Ad using implicit flow.
I registered my application in Azure AD using the Azure Portal > Azure Active Directoy > App Registrations > New Application Registration:
Name = MyWebApi
Application Type = Web app / API
Sign-on URL = http://localhost:55000
Once this app is created, I opened its Manifest file and changed oauth2AllowImplicitFlow from false to true.
Thats all I did for the app registration in azure portal.
Then I called the following URL manually from my chrome browser to get access_token:
https://login.microsoftonline.com/MY-AD-TENANT-GUID/oauth2/v2.0/authorize?client_id=MY-REGISTERED-APP-GUID&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%3A55000&scope=openid&response_mode=fragment
the response from calling the above url is:
http://localhost:55000/#access_token=MY-ACCESS-TOKEN&token_type=Bearer&expires_in=3600&scope=profile+openid+email+00000003-0000-0000-c000-000000000000%2fUser.Read&session_state=b2be972a-cfbc-49f1-bfc0-6c93f6c87d02
when I pass MY-ACCESS-TOKEN as Bearer token in Authorization header to my Web API (.net core 2.2) I get the following exception:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: N-lC0n-9DALqwhuHYnHQ63GeCXc'.
I then tried manually verifying the signature:
when I paste MY-ACCESS-TOKEN in https://jwt.io/ the header is:
{
"typ": "JWT",
"nonce": "AQABAAAAAACEfexXxjamQb3OeGQ4Gugvm6YdOT-bkA0IPllKMt06-J8If5AQ075TVCav94X_ZYcEYKaPneqdJcqYry-Z4XjX0eMN_fiJX_8wXe9D2b6eRiAA",
"alg": "RS256",
"x5t": "N-lC0n-9DALqwhuHYnHQ63GeCXc",
"kid": "N-lC0n-9DALqwhuHYnHQ63GeCXc"
}
I then went to this URL to obtain the public key for kid: N-lC0n-9DALqwhuHYnHQ63GeCXc
https://login.microsoftonline.com/common/discovery/keys
I then pasted the following as a public key on jwt.io to validated token signature:
-----BEGIN CERTIFICATE-----
OBTAINED-PUBLIC-KEY-FROM-THE-ABOVE-URL-HERE
-----END CERTIFICATE-----
and I again get Invalid Signature.
I have been to this thread: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609, but I am not sure why does my token header has nonce value or if this is an issue at all in my case or not.
Any ideas what I am doing wrong here?
I tried this on my side, it worked well.
Request url:
https://login.microsoftonline.com/tenant-name/oauth2/v2.0/authorize?client_id=application_id&response_type=token&redirect_uri=https://snv2app.azurewebsites.net&scope=api://f3d966c0-517e-4e13-a5bb-9777a916b1a0/User.read openid&response_mode=fragment
And when I got the access_token, I parsed it in the jwt.io and entered the public key, I got the result:
What is happening here is the token you are receiving is an access_token for the userInfo endpoint. The audience is graph. Tokens for graph have been modified in a special way so that they must be transformed before the signature can be validated. This allows for graph to forward the token downstream (after transforming) and not worry about a forwarding attack to occur.
To validate see if 'aud == graph'.
My customer sends to me a JWT, I need to validate this JWT using their public key.
I am using Java and JJWT framework to validate this token.
I know decode this token using HS256, but using RS256 I don't know.
their configurations is:
Editing here to improve my question.
The jjwt example of parse that I am using:
Claims String secret = "-----BEGIN CERTIFICATE-----myx5ckey-----END CERTIFICATE-----"
byte[] dataBytes = Base64.getEncoder().encode(secret.getBytes());
byte[] byteKey = Base64.getDecoder().decode(dataBytes);
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(X509publicKey);
Claims body = null;
body = Jwts.parser().setSigningKey(publicKey.getEncoded())
.parseClaimsJws(idToken)
.getBody();
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
How can I validate the received token using the JWKS informations that I show? (imagem above)
I solved my problem.
String secret2 = "myX5c";
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate certificate = cf.generateCertificate(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(secret2)));
PublicKey publicKey = certificate.getPublicKey();
Claims body = null;
body = Jwts.parser().setSigningKey(publicKey)
.parseClaimsJws(idToken)
.getBody();
#KcDoD thanks for your tips.
tldr; Need to follow three steps
Extract public key from JWK may be going through discovery document or from any other mean.
Extract JWS Signature and JWS Signing input as described by JWS specification
Pass public key, JWS signature and JWS Signing input to signature verifier
Depending on the library, step 2 and 3 may be done in single step.!
Long answer
To validate you have to follow the JWS specification.
JWS specification, defined in RFC7515 explains how to create JWT's MAC needed to validate the token. Appendix 2 of the protocol explains how to create a MAC with RS256 and validate it.
Using the discovery information, you must identify the public key. Now here you have received key details as a JWK. According JWK protocol definition on x5x,
The "x5c" (X.509 certificate chain) parameter contains a chain of one
or more PKIX certificates
So basically you have public key in the JWK. Now you need to convert the encoded String of x5x to public key. To do that, please check this already answered question.
Once the public key is constructed use it to validate the token. Following is the extraction from spec.
A.2.2. Validating
Since the "alg" Header Parameter is "RS256", we validate the
RSASSA- PKCS1-v1_5 SHA-256 digital signature contained in the JWS
Signature.
Validating the JWS Signature is a bit different from the previous example. We pass the public key (n, e), the JWS Signature (which is base64url decoded from the value encoded in the JWS representation), and the JWS Signing Input (which is the initial substring of the JWS Compact Serialization representation up until but not including the second period character) to an RSASSA-PKCS1-v1_5 signature verifier that has been configured to use the SHA-256 hash function.
To validate, its better to use a library. For reference, here is a link to how its done using nimbus
How to work with Dwolla API which required Client_id & Client_Secret
https://www.dwolla.com/oauth/rest/users/{account_identifier}?client_id={client_id}&client_secret={client_secret}
I already register Application. And Got Key and Secret
But when I call above described API Endpoint via Fiddler. Got bellow response.
{"Success":false,"Message":"Invalid application credentials.","Response":null}
Note: I tested Client_id = API Key / Client_id = Application Key. But the response remain same. What is the problem ?
The client_id is just another name for the API/Application Key, which identifies your application. The client/application secret is a string that functions as a password for your application. Just like a password, you should never give out your application secret; and if it's ever compromised, let us know immediately and we'll generate a new key/secret pair for you.
About your failed request: Try encoding your application key and secret. If special characters aren't escaped from the URL, the request will be interpreted differently from what you intend.
You can quickly encode the two strings from your Javascript console:
var key = "EUFH378&36%394749D\DWIHD";
encodeURIComponent(key);
Result: "EUFH378%2636%25394749DDWIHD"
var secret = "WOIDJ38&IDI\DK389DDDDD";
encodeURIComponent(secret);
Result: "WOIDJ38%26IDIDK389DDDDD"
And place their encoded equivalents back into your request URL:
https://www.dwolla.com/oauth/rest/users/gordon#dwolla.com?client_id=EUFH378%2636%25394749DDWIHD&client_secret=WOIDJ38%26IDIDK389DDDDD
I am using oauth to authenticate dropbox and download a file from dropbox after getting access_token am using the below signature for download a file from dropbox am passing the root, path of the file, consumerKey and oauth_token with signature_method as PLAINTEXT and am getting an error
{"error": "Bad oauth_signature for oauth_signature_method 'PLAINTEXT'"}
Signature am using is given below :
https://api-content.dropbox.com/1/files?oauth_consumer_key=twcek2m7cxtantc&oauth_signature_method=PLAINTEXT&oauth_token=1jczc39y7rn1265&oauth_version=1.0&path=test%2Fut.txt&root=dropbox&oauth_signature=fbs34nykryouuj1%2526gbwmn3e27g97cfy
What should I do to resolve this error?
I was searching about this and found that:
1) The PLAINTEXT method does not provide any security protection and SHOULD only be used over a secure channel such as HTTPS. It does not use the Signature Base String.
2) The Service Provider declares support for the HMAC-SHA1 signature method for all requests, and PLAINTEXT only for secure (HTTPS) requests.
3) When used with PLAINTEXT signatures, the OAuth protocol makes no attempts to protect User credentials from eavesdroppers or man-in-the-middle attacks. The PLAINTEXT signature algorithm is only intended to be used in conjunction with a transport-layer security mechanism such as TLS or SSL which does provide such protection. If transport-layer protection is unavailable, the PLAINTEXT signature method should not be used.
You can refer this link http://oauth.net/core/1.0/#anchor22
You can also check if your keys are correct
The signature Protocol Parameters are set with the following values unencrypted:
oauth_signature_method : Set to PLAINTEXT.
oauth_signature : Set to the concatenated encoded value of the oauth_consumer_secret parameter and the value of the oauth_token_secret parameter. If the values contain a . character (ASCII code 46), it must be encoded as %2E. The values are separated by a . character (ASCII code 46), even if empty. The result MUST not be encoded again.
For example, if the Consumer Key is dj.9rj$0jd78jf88 and Token Secret is jjd999(j88ui.hs3, the encoded values are:
Consumer Key : dj%2E9rj%240jd78jf88
Token Secret : jjd999%28j88ui%2Ehs3
And the oauth_signature value is dj%2E9rj%240jd78jf88.jjd999%28j88ui%2Ehs3. This value is not encoded anymore and is used as it in the HTTP request. If the Token Secret is empty, the value is dj%2E9rj%240jd78jf88. (the separator . is retained).