Java:
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(),HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey);
byte[] bytes = mac.doFinal(signatureString.getBytes()); return BASE64.encodeBAse64String(bytes);
C#:
public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, signatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) {
string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters);
HMACSHA1 hmacsha1 = new HMACSHA1();
hmacsha1.key = Encoding.ASCII.GetBytes(string.Format("{0} & {1}", UrlEncode(consumerSecret), string.IsNullEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret)));
//data is generated signature base string with attaching all parameters.
byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data);
byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer);
return Convert.ToBase64String(hashBytes);
}
I am creating REST services where I am using OAuth 1.0 signature and for generating Oauth I need to use HMACSHA1 algorithm. My client's API is in C# and my API is in Java, and I have to generate a signature that should be encrypted by my client successfully.
Related
I am trying to validate JWT Access token which generated by KeyCloak Authorization server.
I have copied public_key from
http://ip-address:port/auth/realms/
For validating Access token i am using https://mvnrepository.com/artifact/com.auth0/java-jwt
and referring https://www.baeldung.com/java-jwt-token-decode for verifying signature.
Code I am trying:
String publicKey =<public_key from http://<ip>:<port>/auth/realms/<app> >
String accessToken =<valid access token from keycloak>
String[] chunks = accessToken.split("\\.");
Base64.Decoder decoder = Base64.getDecoder();
String header = new String(decoder.decode(chunks[0])); // {"alg":"RS256","typ" : "JWT","kid" : "dsssdssdf"}
String payload = new String(decoder.decode(chunks[1])); //{"exp":1632237161,"iat":1632236861,"auth_time":1632236854,"jt..."}
String signature = chunks[2]; // <signature from access token>
SignatureAlgorithm sa = SignatureAlgorithm.RS256;
String tokenWithoutSignature = chunks[0] + "." + chunks[1];
SecretKeySpec secretKeySpec = new SecretKeySpec(publicKey.getBytes(), sa.getJcaName());
DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, secretKeySpec);
if (!validator.isValid(tokenWithoutSignature, signature)) {
System.out.println("=============== Invalid access token");
} else {
System.out.println("============= valid access token");
}
After running above code following error observed:
java.lang.IllegalArgumentException: RSA Signature validation requires either a RSAPublicKey or RSAPrivateKey instance.
at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)
at io.jsonwebtoken.impl.crypto.RsaSignatureValidator.<init>(RsaSignatureValidator.java:36)
at io.jsonwebtoken.impl.crypto.DefaultSignatureValidatorFactory.createSignatureValidator(DefaultSignatureValidatorFactory.java:43)
at io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator.<init>(DefaultJwtSignatureValidator.java:37)
at io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator.<init>(DefaultJwtSignatureValidator.java:32)
Can anyone please help here?
I try something like :
String publicKey =<public_key from http://<ip>:<port>/auth/realms/<app> >
byte[] decoded = Base64.getDecoder().decode(publicKey);
String algorithm = "RSA";
KeyFactory kf = KeyFactory.getInstance(algorithm);
PublicKey generatedPublic = kf.generatePublic(new X509EncodedKeySpec(decoded));
String accessToken =<valid access token from keycloak>
String[] chunks = accessToken.split("\\.");
Base64.Decoder decoder = Base64.getDecoder();
String header = new String(decoder.decode(chunks[0])); // {"alg":"RS256","typ" : "JWT","kid" : "dsssdssdf"}
String payload = new String(decoder.decode(chunks[1])); //{"exp":1632237161,"iat":1632236861,"auth_time":1632236854,"jt..."}
String signature = chunks[2]; // <signature from access token>
SignatureAlgorithm sa = SignatureAlgorithm.RS256;
String tokenWithoutSignature = chunks[0] + "." + chunks[1];
DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, generatedPublic);
if (!validator.isValid(tokenWithoutSignature, signature)) {
System.out.println("=============== Invalid access token");
} else {
System.out.println("============= valid access token");
}
and It worked !!
I'm using retrofit for calling my services. One of my services has a header parameter that it signed by SHA256-RSA, but when I invoke my request, I've got the error like this :
java.lang.IllegalArgumentException: Unexpected char 0x1d at 4 in signature value: "|._趿s<�ˠ"
my request method is :
Call<ResponseBody> getToken(#Header("Signature") String Signature,#Header("signature") String signature,#Header("headers") String headers, #Field("username") String username, #Field("password") String password, #Field("grant_type") String grantType);
and my signing method is :
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+", "");
// Base64 decode the result
byte[] pkcs8EncodedBytes = Base64.decode(pkcs8Pem, Base64.DEFAULT);
// extract the private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privKey);
signature.update(message.getBytes());
return signature.sign();
could you help with that?
there are several characters, that could cause this error, for example an unvisible newline character, as stated in this github issue:
https://github.com/square/retrofit/issues/1153
This is forbidden by okhttp Client, which is utilized by retrofit to make http calls.
I would recommend you log the lower case signaturer esult and check directly which character is causing the trouble.
Best of luck!
I am having problems generating a valid signature key for doing HTTP Post in browser- I keep getting errors like
The request signature we calculated does not match the signature you provided. Check your key and signing method
and since that doesn't tell me anything much about where the problem lies, i decided to debug if my signature calculation method is correct.
So I decided to replicate the example in the documentation using the given String to sign and the Secret Access Key. My generated signature doesn't match the signature given on the documentation page.
What am I doing wrong here ? The below is my existing code (I am using AWS Signature V4)
private void debugSignatureGeneration() throws Exception {
byte[] testSigningKey = getSigningKey("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "20151229", "us-east-1",
"s3");
String testStringToSign = "eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9";
String testSignature = getSignatureV4(testStringToSign, testSigningKey);
System.out.println("test signature " + testSignature);
}
static String getSignatureV4(String stringToSign, byte[] signingKey) throws Exception {
byte[] signature = HmacSHA256(stringToSign, signingKey);
return Hex.encodeHexString(signature);
}
static byte[] HmacSHA256(String policy, byte[] key) throws Exception {
String algorithm = "HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(policy.getBytes("UTF8"));
}
static byte[] getSigningKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
If it helps, the signature that is given in the documentation is
46503978d3596de22955b4b18d6dfb1d54e8c5958727d5bdcd02cc1119c60fc9
while the signature I am able to get is
8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e
This is the code am using on the Java side
private String encodedHexString(String secretKey, String payload)
throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(),"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(keySpec);
byte[] payloadDigest = mac.doFinal(payload.getBytes());
String encodedDigest = DatatypeConverter.printHexBinary(payloadDigest);
return encodedDigest;
}
where secretKey is the Secret token entered on GitHub side as well on my side and the payload is what I am getting in the request.getParameter("payload") for application/x-www-form-urlencoded Content-type.
matching this with request.getHeader("X-Hub-Signature") don't match unfortunately, even after appending "sha1=" to it on Java side.
I had success with simply encoding the whole request body, without any kind of parameter parsing, unescaping, etc:
String secret = "73fcce1ecaeeb4136f27854eaaacb785929bb9a3";
String payload = "payload=%7B%22zen%22%3A%22It%27s+not+fully+shipped+until+it%27s+fast.%22%2C%22hook_id%2...";
System.out.println(encodedHexString(secret, payload));
The result was:
0F026F9E5107F4BF377CA032C1431BFED687B6E9
This matched the X-Hub-Signature header very well:
X-Hub-Signature: sha1=0f026f9e5107f4bf377ca032c1431bfed687b6e9
Based on nimbus-jose-jwt Java library, I tried to create the following JSON Web Token (JWT) and signed it using a JSON Web Signature (JWS) using a string "secret" hashed to SHA256.
But after generating serialized string and test it at jwt.io, i always get the error "Invalid Signature".
When I try to decode at server side using Python decoder, I also get signature error. What could be wrong?
byte[] bytes = new byte[32];
String message = "secret";
MessageDigest md = MessageDigest.getInstance("SHA-256");
bytes = md.digest(message.getBytes("UTF-8"));
JWSSigner signer = new MACSigner(bytes);
// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet();
claimsSet.setSubject("alice");
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
// Apply the HMAC
signedJWT.sign(signer);
// To serialize to compact form, produces something like
String s = signedJWT.serialize();
It looks like you are using the SHA-256 digest of "secret" as the key to create the MAC, and plain old "secret" for validating. Replace:
byte[] bytes = new byte[32];
String message = "secret";
MessageDigest md = MessageDigest.getInstance("SHA-256");
bytes = md.digest(message.getBytes("UTF-8"));
JWSSigner signer = new MACSigner(bytes);
with:
JWSSigner signer = new MACSigner("secret");