Consuming a web service with usernametoken profile with pwd digest - java

guy's, pleeaseee help me!!!
I created a web service which use UserNameToken Profile security with Password type like 'digest'. When i try to use the webservice from SOAP-UI (sending the information in the head of the xml) i can consume the webservice normally, but when i'm trying consume this webservice with JAVA the server don't authenticate the user.
I try to use axis and JAX-WS (send the information in the head of the envelop - like SOAP-UI) but the server don't authenticate the user.
Can someone help me?
Here is my code:
public void faz() throws NoSuchAlgorithmException, Exception {
/////////////////////////////////////////////////////////////
//get the timestamp, nonce and password in SHA-1 and BASE64//
/////////////////////////////////////////////////////////////
String nonce, timestamp, secret;
nonce = String.valueOf(this.hashCode());
BASE64Encoder encoder2 = new BASE64Encoder();
nonce = encoder2.encode(nonce.getBytes());
Calendar c = Calendar.getInstance();
c.setTime(new Date());
timestamp = DatatypeConverter.printDateTime(c);
timestamp = timestamp.substring(0, 19);
timestamp = timestamp+"Z";
secret = "weblogic1";
MessageDigest SHA1 = null;
try {
SHA1 = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
;
String beforeEncryption = nonce + timestamp + secret;
try {
SHA1.reset();
byte[] toEncrypt = beforeEncryption.getBytes("UTF-8");
SHA1.update(beforeEncryption.getBytes());
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
byte[] encryptedRaw = SHA1.digest();
byte[] encoded = Base64.encodeBase64(encryptedRaw);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update("password".getBytes());
BASE64Encoder encoder = new BASE64Encoder();
String senha = encoder.encode(digest.digest());
System.err.println(senha);
////////////////////////////////////
//////////END //////////////////////
////////////////////////////////////
CalculaServiceService ss = new CalculaServiceServiceLocator();
CalculaService service = ss.getCalculaServicePort();
String uri = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
String uriCrea = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
SOAPHeaderElement securityE = new SOAPHeaderElement(uri, "Security",
null);
SOAPHeaderElement tokenE = new SOAPHeaderElement(uri, "UsernameToken",
null);
SOAPHeaderElement userE = new SOAPHeaderElement(uri, "Username", null);
tokenE.setObjectValue(null);
securityE.setObjectValue(null);
userE.setValue("username");
SOAPHeaderElement pwdE = new SOAPHeaderElement(uri, "Password", null);
pwdE.addAttribute(uri, "Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
pwdE.setValue(senha);
SOAPHeaderElement nonceE = new SOAPHeaderElement(uri, "Nonce", null);
nonceE.addAttribute(uri, "EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
nonceE.setValue(nonce);
SOAPHeaderElement createdE = new SOAPHeaderElement(uriCrea, "Created", null);
createdE.setValue(timestamp);
tokenE.addChildElement(userE);
tokenE.addChildElement(pwdE);
tokenE.addChildElement(nonceE);
tokenE.addChildElement(createdE);
securityE.addChildElement(tokenE);
((Stub) service).setHeader(securityE);
service.calcula(13, 10, "somar");
}

WS-Security standard defines the password digest as Base64 ( SHA-1 ( nonce + created + password ) ).
You reflect this correctly by hashing the content of beforeEncryption variable and store the result to encryptedRaw and then base64-encode it to encoded variable. So far so good. But the encoded variable is not used anywhere. For some reason, you compute a second digest base64(sha1("password")) and store it to senha variable that is used later to fill the WSS Password element.
Completely remove the code that computes the second digest and use the content of the encoded variable:
Conceptually, change the pwd.setValue() from:
pwdE.setValue(senha)
To:
pwdE.setValue(encoded)
and make sure that encoded variable will be a String, not a byte array.
Additionally, you should consider to use a better value for nonce than an object hash-code.

Related

Verify Access token signature using java-jwt

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 !!

how to parse jwt sign key from apple p8 file with Java

Now I want to generate an JWT token to request apple server follow this docs, I have the p8 file download from apple, how to get jwt sign key from p8 file? this is my jwt token generate code:
Map<String,Object> jwtHeader = new HashMap<>();
jwtHeader.put("alg","ES256");
jwtHeader.put("kid","YDKL424AF9");
jwtHeader.put("typ","JWT");
Map<String,Object> appleJwtPayload = new HashMap<>();
appleJwtPayload.put("iss","5fb8e836-27d7-4390-8f40-008acd64a29d");
appleJwtPayload.put("iat",System.currentTimeMillis() / 1000L);
appleJwtPayload.put("exp",System.currentTimeMillis() / 1000L + 60 * 15);
appleJwtPayload.put("aud","appstoreconnect-v1");
appleJwtPayload.put("nonce",UUID.randomUUID().toString());
appleJwtPayload.put("bid","com.earth.dolphin");
String appleKey = "<how to get the apple key>";
SecretKey secretKey = new SecretKeySpec(appleKey.getBytes(), SignatureAlgorithm.ES256.getJcaName());
String accessToken = Jwts.builder()
.setClaims(appleJwtPayload)
.setHeader(jwtHeader)
.signWith(secretKey)
.compact();
I read the KeyStore code follow this question, but I still could not figure out how to do it, any one could help me?
get the sign key like this:
byte[] p8der = Files.readAllBytes(Path.of("/opt/apps/dolphin-post/AuthKey_YDKL424AF9.p8"));
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new org.apache.commons.codec.binary.Base64().decode(p8der));
PrivateKey appleKey = KeyFactory.getInstance("EC").generatePrivate(priPKCS8);
the file AuthKey_YDKL424AF9.p8 was download from apple, you should remove the begin and end header of the file. This is my full function to get private key:
public static PrivateKey getPrivateKey(String filename, String algorithm) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Java did not support the algorithm:" + algorithm, e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("Invalid key format");
}
}
you can use it like this:
PrivateKey appleKey = SecurityUtil.getPrivateKey(APPLE_PRIVATE_KEY_PATH,"EC");
the APPLE_PRIVATE_KEY_PATH in my OS is:
apple.private.key.path=/opt/apps/dolphin-post/AuthKey.p8
changed it to your path.

Azure Notification Hub - Invalid authorization token signature with Java

Hi all I'm trying to send a notification with Azure Notification Hub in Java.
This is the code I used:
NotificationHub hub = new NotificationHub("Endpoint=sb://XXXXXXXXXX;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=XXXXXXXXXXXXXXXXX", "myHubName");
String message = "{\"data\":{\"msg\":\"Hello from Java!\"}}";
Notification n = Notification.createGcmNotification(message);
NotificationOutcome result = hub.sendNotification(n);
return result.getNotificationId();
The problem is that I get an Invalid authorization token signature error when I try to get the NotificationOutcome object.
I assume that the problem is in the generation of the token, which is generated with the following method of NotificationHub class:
private String generateSasToken(URI uri) {
String targetUri;
try {
targetUri = URLEncoder
.encode(uri.toString().toLowerCase(), "UTF-8")
.toLowerCase();
long expiresOnDate = System.currentTimeMillis();
expiresOnDate += SdkGlobalSettings.getAuthorizationTokenExpirationInMinutes() * 60 * 1000;
long expires = expiresOnDate / 1000;
String toSign = targetUri + "\n" + expires;
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = SasKeyValue.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(toSign.getBytes("UTF-8"));
// Convert raw bytes to Hex
String signature = URLEncoder.encode(
Base64.encodeBase64String(rawHmac), "UTF-8");//
// construct authorization string
String token = "SharedAccessSignature sr=" + targetUri + "&sig="
+ signature + "&se=" + expires + "&skn=" + SasKeyName;
System.out.println(token);
return token;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
I tried the same example using php and it worked. Any suggestions? Thanks in advance.

Need help creating a valid nonce

I am trying to consume a web service that uses Password Digest mode, and I have these functions in my Java application to generate a random nonce, creation date and password digest. I can't get past the Authentication Failed error, and the documentation isn't overly clear on whether they want SHA-1 or MD5, as it mentions both in passing. I've tried MD5 instead of SHA-1 and I am getting the same result. I managed to get the requests to work via a test on SoapUI, but I have no idea how that application is generating the digest / nonce. Any help is appreciated.
Here's the code I am using to generate the nonce and the password digest:
private static SOAPMessage createSOAPRequest() throws Exception
{
String password = "FakePassword";
String nonce = generateNonce();
System.out.println("Nonce = " + nonce);
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date today = Calendar.getInstance().getTime();
String created = dateFormatter.format(today);
System.out.println("Created = " + created);
String passwordDigest = buildPasswordDigest(nonce, created, password);
System.out.println("Password Digest = " + passwordDigest);
}
private static String buildPasswordDigest(String nonce, String created, String password) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
MessageDigest sha1;
String passwordDigest = null;
try
{
sha1 = MessageDigest.getInstance("SHA-1");
sha1.update(Base64.decodeBase64(nonce));
sha1.update(created.getBytes("UTF-8"));
passwordDigest = new String(Base64.encodeBase64(sha1.digest(password.getBytes("UTF-8"))));
sha1.reset();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return passwordDigest;
}
private static String generateNonce() throws NoSuchAlgorithmException, NoSuchProviderException, UnsupportedEncodingException
{
String dateTimeString = Long.toString(new Date().getTime());
byte[] nonceByte = dateTimeString.getBytes();
return Base64.encodeBase64String(nonceByte);
}
The solution was to replace the line sha1.update(nonce.getBytes("UTF-8")); with sha1.update(Base64.decodeBase64(nonce));

Generated JSON Web Signature in Java produce invalid signature

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");

Categories