I am working to integrate my application with the Withings api with spring rest template.
But,while generating the Oauth signature I am getting the "Invalid signature". I am trying to devise a signature according to the API specification but I am not able to generate it successfully. I have mentioned the code that I used. Please, provide me some solutions.
private String generateSignature(String baseString, String secret) throws UnsupportedEncodingException {
String secretKey = consumerSecret + "&";
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA1SignatureMethod.SIGNATURE_NAME);
HMAC_SHA1SignatureMethod hmacsha = new HMAC_SHA1SignatureMethod(keySpec);
String signatureString = hmacsha.sign(baseString);
String base64Encode = new String((signatureString.getBytes()));
signature = URLEncoder.encode(base64Encode, "UTF-8");
For reference, http://oauth.withings.com/api
i faced the same issues before , it seems the signature need you params ( api params + oauth params ) to be ordered alphabetically .
You need also to give a correct secret word in making you signature based uri .
you can check if you want my php oauth lib ( more particulary in AbstractService.php ) for withing here
https://github.com/huitiemesens/PHPoAuthLib ( it s a fork of the original phpoauthlib with specific order for withings apis ... )
Related
I am trying to create an app in flutter and the sample code which I have is in java.
Here is the sample java code gist
https://gist.github.com/kapiljhajhria/72a22ff75e238878f539f7bb21026208
and here is my flutter code gist
https://gist.github.com/kapiljhajhria/795d1a7c7cf1c76ca8e327bf8b2f51de
Here is a brief summary of what I am doing
Generate a unique Session Key: AES Random Key 256
Encrypt JSON data using the Session Key from step 1
Generate SHA256 hash of JSON data
Encrypt generated hash from step 3, using session key from step 1.
Encrypt the session key using the public key. Public key provided as certificate.cer file. I copied the String value and added it to the class as a constant in order to make it easier to use. Not sure if this was the best approach.
Created a POST request with 3 Parameters. As shown in the java code. I think I am doing this part correctly.
The response which I will get will be encrypted using the session key from step 1. So i will have to decrypt that response data. Haven't reached this step yet.
I don't have access to the server where this request is being made.
Since the post request is being made using web view, I can't figure out a way to get proper error from my request. All I get is web page which says "Invalid Request"
So My first guess is that I am not using public key properly to encrypt my session key.
if that part is correct, then I am not encrypting the data properly or my encryption method doesn't match encryption methods used in java code the java code. Maybe the session key which I am generating is not correct.
Any help would be greatly appreciated. Thank you. If you need anything from me then please let me know.
I used this document as my reference: https://developers.emsigner.com/signer-gateway/api-reference/signing-documents.html
You need 2 packages: pointycastle and x509, and to import them as follows:
import 'package:pointycastle/export.dart';
import 'package:x509/x509.dart';
Then you need these helper functions:
Uint8List generateSessionKey() {
final r = Random();
return Uint8List.fromList(List<int>.generate(32, (_) => r.nextInt(256)));
}
RSAPublicKey parseCert(String pemData) {
final cert = parsePem(pemData).first as X509Certificate;
final pub = cert.publicKey as RsaPublicKey;
return RSAPublicKey(pub.modulus, pub.exponent);
}
Uint8List encryptUsingPublicKey(RSAPublicKey key, Uint8List data) {
final cipher = PKCS1Encoding(RSAEngine())
..init(true, PublicKeyParameter<RSAPublicKey>(key));
return cipher.process(data);
}
Uint8List encryptUsingSessionKey(Uint8List key, Uint8List data) {
final cipher = PaddedBlockCipher('AES/ECB/PKCS7')
..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
return cipher.process(data);
}
Uint8List sha256Digest(Uint8List data) {
return SHA256Digest().process(data);
}
And you'd build your 3 parameters like this:
final pem = File('cert2.pem').readAsStringSync();
final publicKey = parseCert(pem);
final sessionKey = generateSessionKey();
final encryptedSessionKey = encryptUsingPublicKey(publicKey, sessionKey);
final jsonString = json.encode(<String, dynamic>{
'FileType': 'PDF',
'SignaturePosition': 'Top-Left',
'AuthToken': 'some token',
'File': '',
'SUrl': 'http://localhost:3000/Success',
'FUrl': 'http://localhost:3000/Error',
'CUrl': 'http://localhost:3000/Cancel',
'ReferenceNumber': 'generate unique reference number',
});
final jsonBytes = utf8.encode(jsonString) as Uint8List;
final encryptedJson = encryptUsingSessionKey(sessionKey, jsonBytes);
final hash = sha256Digest(jsonBytes);
final encryptedHash = encryptUsingSessionKey(sessionKey, hash);
final p1 = base64.encode(encryptedSessionKey);
final p2 = base64.encode(encryptedJson);
final p3 = base64.encode(encryptedHash);
BUT, the big problem I see is how you then do the POST, because you want to be in a web page, right? And the normal flutter web view doesn't support an initial post. It does look like there's another package. Just search for flutter webview post.
By the way, if you don't want to use the pointycastle registry, you can rewrite encryptUsingSessionKey without as:
final cipher = PaddedBlockCipherImpl(
PKCS7Padding(),
ECBBlockCipher(AESEngine()),
)..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
return cipher.process(data);
Finally, at least until you get the web view issue understood, you can just use http to do the post. But, let it do the work of encoding the parameters and setting the content type, as follows:
final response = await http.post(
Uri.parse('https://somewhere/V3_0/Index'),
body: <String, String>{
'Parameter1': p1,
'Parameter2': p2,
'Parameter3': p3,
},
);
print(response.statusCode);
print(response.body);
Have a look at this project, It's an example for the Encryption by Flutter with a good documentation, have 3 types of encryptions:
AES encryptions
Fernet encryptions
Salsa20 encryptions
I want to pass a JWT token from a NodeJS service to another service in Java. However, based on the what I've tried, the token is always invalid whenever I try to verify it on the Java side. I do understand the JWT is platform-independent but I'm not able to figure out why is the token not able to get verified on the Java side.
Error: io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted
NodeJS signing the token (using express-jwt library)
const jwtPayload = { id: "admin" };
const secret = " ... some secret ...";
const jwtData = { expiresIn: 2h };
const access_token = jwt.sign(jwtPayload, secret, jwtData);
Java verifying the token (using io.jsonwebtoken)
String secret = "...same as on the nodejs side"
String accessToken = " .. access_token from nodejs .. "
String username = Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(accessToken).getBody().getSubject();
I have not set any additional options either on the NodeJS side or the Java side. Am I missing some configuration on either of the platforms? Or should I be using a different library?
In my case, solved like this:
GENERATE JWT IN EXPRESS:
jwt.sign({ issuer: new Date(), roles, exp: Math.floor(Date.now() / 1000) + (43200) }, SECRET, {
algorithm: 'HS256',
subject: username,
jwtid: jwtId,
}, (err, token) => {
if (err) {
logger.error(util.inspect(err));
}
resolve({ token, jwtId });
});
IN JAVA I READ USING "SECRET".getBytes("UTF-8"):
Claims claims = Jwts.parser().setSigningKey("SECRET".getBytes("UTF-8")).parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
I think the issue is the string secret. By calling getBytes("UTF-8") on your secret and providing signWith() with the byte[], everything will work out.
String username = Jwts.parser()
.setSigningKey(secret.getBytes("UTF-8"))
.parseClaimsJws(accessToken)
.getBody()
.getSubject();
(Also need to catch the UnsupportedEncodingException!)
I am in the process of migrating users from an OAuth 2 system made with Symfony to Keycloak.
Create the users in Keycloak with the encrypted password is ok, but I can't find an algorithm equivalent to mine.
example of user creation:
Post
{
"firstName": "test_encryption",
"lastName":"test_encryption",
"email":"jeremy.rafflin.test#ageo.fr",
"credentials": [{
"type":"password",
"secretData":"{\"value\":\"zeR2Uapc+a/2qD5QR56gh3mVb+KOeZ2XU+rkWMK6B5A=\",\"salt\":\"OThjajM1WnVZWlI3UzZOLk12WjJsQS9VWWZXQXp0WGZGLm5tL2hGSVFzbw==\"}",
"credentialData": "{\"algorithm\":\"sha512\",\"hashIterations\":5000}"
}]
}
For the current encryption in PHP I am using https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php.
Which amounts to:
$password = 'toto';
$salt = '1234';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
for ($i=1; $i<5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encodedPassword = base64_encode($digest);
In the spring documentation, https://andifalk.github.io/reactive-spring-security-5-workshop/workshop-tutorial.html I see :
package org.springframework.security.crypto.factory;
public class PasswordEncoderFactories {
...
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new LdapShaPasswordEncoder());
encoders.put("MD4", new Md4PasswordEncoder());
encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new StandardPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
...
}
So I thought my algorithm matched MessageDigestPasswordEncoder ("SHA-512") and tried to create a user, but it doesn't work:
"credentialData": "{"algorithm":"SHA-512","hashIterations":5000}"
Does my algorithm exist in keycloak or do I have to create a custom credential algorithm ?
Does my algorithm exist in keycloak or do I have to create a custom
credential algorithm ?
From the Keycloak Documentation:
Here’s an explanation of each policy type:
HashAlgorithm
Passwords are not stored as clear text. Instead they are
hashed using standard hashing algorithms before they are stored or
validated. The only built-in and default algorithm available is
PBKDF2.
Nevertheless, Keycloak allows you to customized and deploy your own algorithm by taking advantage of Service Provider Interfaces.
I'm trying to integrate Apple Map Web Snapshot which needs a signature query parameter in the URL. I able to successfully generate and validate ES256 signature in JWA package from NPM but not in Java. Please help me on finding equivalent lib to generate valid signature, I have tried few JWA libs in Java.
// Required modules.
const { readFileSync } = require("fs");
const { sign } = require("jwa")("ES256");
/* Read your private key from the file system. (Never add your private key
* in code or in source control. Always keep it secure.)
*/
const privateKey = readFileSync("[file_system_path]");
// Replace the team ID and key ID values with your actual values.
const teamId = "[team ID]";
const keyId = "[key ID]";
// Creates the signature string and returns the full Snapshot request URL including the signature.
function sign(params) {
const snapshotPath = `/api/v1/snapshot?${params}`;
const completePath = `${snapshotPath}&teamId=${teamId}&keyId=${keyId}`;
const signature = sign(completePath, privateKey);
// In this example, the jwa module returns the signature as a Base64 URL-encoded string.
// Append the signature to the end of the request URL, and return.
return `${completePath}&signature=${signature}`;
}
// Call the sign function with a simple map request.
sign("center=apple+park")
// The return value expected is: "/api/v1/snapshot?center=apple+park&teamId=[team ID]&keyId=[key ID]&signature=[base64_url_encoded_signature]"
Apache CXF - This lib generates similar to JWA module in node but failed to authenticate.
String teamId = [Team Id];
String keyId = [Key id];
String privateKey = [private key path];
String privateKeyContent = getKeyFileContent(privateKey);
String API_VERSION_PATH = "/api/v1/snapshot?";
String param = [QueryParam];
//example -> param = "center=[city,country or lat,lang]&size=90x90&lang=en&radius=2";
String params = param + "&teamId="+ teamId + "&keyId=" + keyId;
String payload = API_VERSION_PATH + params;
PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
Base64.decodeBase64(privateKeyContent)));
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
//compactProducer.getJwsHeaders().setKeyId(keyId);
compactProducer.signWith(key);
String signed = compactProducer.getEncodedSignature();
String encodedSignature = new String(Base64.encodeBase64URLSafe(compactProducer.getEncodedSignature().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
JJWT - This lib generates big signature then the signature generated in node module.
String signed = new String(Base64.encodeBase64URLSafe(Jwts.builder().setPayload(payload)
.signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, key).compact().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
sample output signature
compactProducer.getEncodedSignature() signed --> qQ5G9_lwGJ9w158FVSmtPx_iH43xlg2_gx9BlHEJbER73xpAeIHtDRnT8wnveH_UEPxNe7Zgv4csJ48Oiq-ZIQ
Base64.encodeBase64URLSafe(signature) --> cVE1RzlfbHdHSjl3MTU4RlZTbXRQeF9pSDQzeGxnMl9neDlCbEhFSmJFUjczeHBBZUlIdERSblQ4d252ZUhfVUVQeE5lN1pndjRjc0o0OE9pcS1aSVE
JJWT signed -> ZXlKaGJHY2lPaUpGVXpJMU5pSjkuTDJGd2FTOTJNUzl6Ym1Gd2MyaHZkRDlqWlc1MFpYSTlRM1Z3WlhKMGFXNXZMRlZUUVNaMFpXRnRTV1E5V0ZaWU5GWlhSbEZUTXlaclpYbEpaRDFWUVRWTlNGWlhWMWhMLlExUEtoeGwzSjFoVWVUWGtmeXRLckliYm5zeDdZem5lZVpxTVc4WkJOVU9uLVlYeFhyTExVU05ZVTZCSG5Xc3FheFd3YVB5dlF0Yml4TVBSZGdjamJ3
The signature in the NodeJS code is generated by the jwa('ES256')#sign method, which has the following functionality:
ES256: ECDSA using P-256 curve and SHA-256 hash algorithm [1].
The signature will be the pair (r, s), where r and s are 256-bit unsigned integers [2].
The signature is base64url-encoded [3].
Ad 1: A corresponding implementation for ES256 is possible in Java using on-board means (SunEC provider, Java 1.7 or higher), [4]:
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initSign(privateKey);
String payload = "The quick brown fox jumps over the lazy dog";
ecdsa.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureDER = ecdsa.sign();
Here privateKey is the private key of type java.security.PrivateKey, analogous to key in the CXF code.
Ad 2: The Java code returns the signature in the ASN.1 DER format and must therefore be converted into the (r,s) format [5]. Either a user-defined method can be implemented or a method from a supporting library can be used, e.g. the method com.nimbusds.jose.crypto.impl.ECDSA.transcodeSignatureToConcat of the Nimbus JOSE + JWT library [6][7][8]:
byte[] signature = transcodeSignatureToConcat(signatureDER, 64);
Ad 3: Base64url encoding is possible in Java with on-board means [9]:
String signatureBase64url = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
Since a different signature is generated each time, a direct comparison of the signatures generated in both codes isn't possible. However, compatibility with the jwa-npm library can be tested by verifying the signature generated in the Java code with the jwa-npm library:
const jwa = require("jwa");
const ecdsa = jwa('ES256');
var message = "The quick brown fox jumps over the lazy dog";
var verify = ecdsa.verify(message, signatureBase64url, publicKey);
Here, signatureBase64url is the signature generated with the Java code. publicKey is the corresponding public key in X.509 PEM format (-----BEGIN PUBLIC KEY-----...) [10].
The functionality of the jwa('ES256')#sign method is different from that of the posted JJWT or Apache CXF code: The last two generate a JWT [11]. The header is the base64url encoding of {"alg": "ES256"}. Accordingly the signature is that for the Base64url encoded header and the Base64url encoded payload, both separated by a dot:
String payload = "The quick brown fox jumps over the lazy dog";
//JJWT
String jwtJJWT = Jwts.builder().setPayload(payload).signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, privateKey).compact();
//CXF
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
String jwtCXF = compactProducer.signWith(privateKey);
String signatureCXF = compactProducer.getEncodedSignature(); // signature, 3. portion of JWT
Example for a JWT generated by this:
eyJhbGciOiJFUzI1NiJ9.VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw.rcrzqr3ovu7SH9ci-S6deLn6BuiQkNv9CmeOnUPwva30pfK9BOX0YOgjZP5T08wjxCBTTHV3aex0M76toL8qpw
This question already has answers here:
JWT (JSON Web Token) library for Java [closed]
(9 answers)
Closed 3 years ago.
I have to create my token with JWT but I don't know to do it.
You have to use a library for that.
I personally use nimbus-jose-jwt.
This is an example from their page using HS256 for sign the JWT:
// Generate random 256-bit (32-byte) shared secret
SecureRandom random = new SecureRandom();
byte[] sharedSecret = new byte[32];
random.nextBytes(sharedSecret);
// Create HMAC signer
JWSSigner signer = new MACSigner(sharedSecret);
// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet();
claimsSet.setSubject("alice");
claimsSet.setIssuer("https://c2id.com");
claimsSet.setExpirationTime(new Date(new Date().getTime() + 60 * 1000));
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
// Apply the HMAC protection
signedJWT.sign(signer);
// Serialize to compact form, produces something like
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
String s = signedJWT.serialize();
You can also use jose4j.
An example from their page using RSA for sign the JWT (public + secret key):
// Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// Give the JWK a Key ID (kid), which is just the polite thing to do
rsaJsonWebKey.setKeyId("k1");
// Create the Claims, which will be the content of the JWT
JwtClaims claims = new JwtClaims();
claims.setIssuer("Issuer"); // who creates the token and signs it
claims.setAudience("Audience"); // to whom the token is intended to be sent
claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now)
claims.setGeneratedJwtId(); // a unique identifier for the token
claims.setIssuedAtToNow(); // when the token was issued/created (now)
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
claims.setSubject("subject"); // the subject/principal is whom the token is about
claims.setClaim("email","mail#example.com"); // additional claims/attributes about the subject can be added
List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
claims.setStringListClaim("groups", groups); // multi-valued claims work too and will end up as a JSON array
// A JWT is a JWS and/or a JWE with JSON claims as the payload.
// In this example it is a JWS so we create a JsonWebSignature object.
JsonWebSignature jws = new JsonWebSignature();
// The payload of the JWS is JSON content of the JWT Claims
jws.setPayload(claims.toJson());
// The JWT is signed using the private key
jws.setKey(rsaJsonWebKey.getPrivateKey());
// Set the Key ID (kid) header because it's just the polite thing to do.
// We only have one key in this example but a using a Key ID helps
// facilitate a smooth key rollover process
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
// Set the signature algorithm on the JWT/JWS that will integrity protect the claims
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
// Sign the JWS and produce the compact serialization or the complete JWT/JWS
// representation, which is a string consisting of three dot ('.') separated
// base64url-encoded parts in the form Header.Payload.Signature
// If you wanted to encrypt it, you can simply set this jwt as the payload
// of a JsonWebEncryption object and set the cty (Content Type) header to "jwt".
String jwt = jws.getCompactSerialization();
In the question that Erik Gillespie indicates are more options.
The jwt.io page allows you to paste the generated token and see his payload. Also, if you put your secret key it would say if the integrity of the token.
Try using solution mentioned in below url:
https://dev.to/keysh/spring-security-with-jwt-3j76
You can use following method:
public String jwtToken(String name) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
Date expireDate = new Date(nowMillis);
Key key = MacProvider.generateKey();
String compactJws = Jwts.builder()
.setSubject(name)
.setAudience("users")
.setIssuedAt(now)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, key)
.signWith(S)
.compact();
return compactJws;
}