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.
Related
I have to send the AES encrypted json as content to web server. But after decryption, the content has extra trash symbols appeared at the beggining of the line.
My test method creates the object that is serialized and being send:
[TestMethod]
public void SendModeChangeWorksAsExpected()
{
var snpashot2Send = new ModeChangedReport
{
ControlWorkMode = ModeEnumeration.Stopped,
//Controls
ControlDate = DateTime.Now,
IsSent = false,
SentTime = null,
ReportType = ReportType.ModeChanged,
Line = new Line
{
AgencyId = "a799eb4f-86da-4af1-a221-9ed8b741b5ce"
}
};
//Создаём шифрованное значение
var encryptedString = _agencyReportEncriptingTranslator.ConvertModeChange2CypheredString(snpashot2Send);
//Отправляем в Агентство и получаем результат
var value = _agencyClient.SendModeChangeReport(encryptedString);
}
Here are the serialization and encrypt methods:
public string ConvertModeChange2CypheredString(ModeChangedReport report)
{
if (report == null) throw new ArgumentNullException(nameof(report));
//obj to json
var json = new ModeChangedReportJson
{
LineId = report.Line.AgencyId,
Mode = CreateModeFromIktToUzbekistan(report.ControlWorkMode),
ActionDate = ConvertDateToAgencyString(report.ControlDate)
};
//Serialization
var retString = _agencyJsonSerializer.SerializeReport2Json(json);
//Шифруем сериализованный json
var cypheredValue = _encryptionService.EncryptString(retString);
return cypheredValue;
}
Encrypt method:
public string EncryptString(string plaintext)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plaintext);
var cypheredTextBytes = Encrypt(plainTextBytes);
var converted2Base64Value = Convert.ToBase64String(cypheredTextBytes);
return converted2Base64Value;
}
private byte[] Encrypt(byte[] bytes)
{
#region Args Validation
if (bytes == null || bytes.Length < 1)
{
throw new ArgumentException("Invalid bytes to encrypt");
}
if (_key == null || _key.Length < 1)
{
throw new InvalidOperationException("Invalid encryption key");
}
#endregion
byte[] encrypted;
try
{
using (AesManaged aes = new AesManaged())
{
aes.Key = _key;
aes.IV = _iv;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, _iv);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length);
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(bytes, 0, bytes.Length);
}
encrypted = ms.ToArray();
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return encrypted;
}
Http client send method:
public bool SendModeChangeReport(string cypheredValue)
{
var token = GetAccessToken();
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AuthorizationToken);
client.DefaultRequestHeaders.Add("DEVICE_ID", _agencyAppSettings.DeviceId);
var content2Post = new StringContent(cypheredValue, Encoding.UTF8, "application/json");
using (var response = client.PostAsync(_agencyAppSettings.SendModeChangedReportUrl, content2Post).Result)
{
string tokenResponse = null;
try
{
tokenResponse = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
return true;
}
catch (Exception ex)
{
_eventLogManager.LogError("При попытке отправить отчёт о смене режима, произошла ошибка: "
+ $"Код: {response.StatusCode}. Контент: {tokenResponse}. Ошибка: {ex.Message}.");
return false;
}
}
}
}
After decryption on receiving server, the string grows with extra trash characters at the beginning, like G���h R��EQ�Z {"lineid":"a799eb4f-86da-4af1-a221-9ed8b741b5ce"...
The decrypt method of the server (Java):
I think that the problem is the padding difference: PKCS7 on my side, and PKCS5 on server.
How can I solve this problem with the extra chars appear on server side?
Those aren't trash characters, they're the Unicode Replacement Character returned when bytes are decoded into text using the wrong character set.
The very fact you got readable text means decrypting succeeded. It's decoding the bytes into text that failed.
The bug is in the Java code. It's using the String(byte[]) which, according to the docs:
Constructs a new String by decoding the specified array of bytes using the platform's default charset.
That's obviously not UTF8. The String(byte[] bytes,Charset charset) or String(byte[] bytes,String charsetName) constructors should be used instead, passing the correct character set, eg :
byte[] decryptedBytes = cipher.doFinal(....);
return new String(decryptedBytes, StandardCharsets.UTF_8);
The hacky alternative is to change the remote server's default character set to UTF8.
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 a newbie in Java, I'm trying to sent a byte [] array via socket, but it itself convert my array to string and then sends it. so far it's ok, but the problem is that I need to either receive the message in array type, OR I have to convert the string to array, in order to make the decrypt method be able to decrypt the array (the input of decrypt method must be byte array). how can I do that?
my serverside related code is:
private void IssuingTickets() throws Exception{
String socketUsername = reader.readLine();//rcv username
String socketPassword = reader.readLine();//rcv password
writer.println("Lemme Check!");
Properties prop = new Properties();
prop.load(new FileInputStream("input"));
String fileUsername = prop.getProperty("username");
String filePassword = null;
if (prop.getProperty("password") != null) {
filePassword = prop.getProperty("password");}
if (socketPassword.equals(filePassword)){
String sessionKeyBobKdc = new Scanner(new File("sBOBandKDC")).useDelimiter("\\Z").next();
byte[] ClientTicket = encrypt(sessionKeyBobKdc, filePassword);
System.out.println("clietn ticket = " + ClientTicket+" ArraytoString " + Arrays.toString(ClientTicket));
writer.println(ClientTicket);
String KDCkey = new Scanner(new File("KDCkey")).useDelimiter("\\Z").next();
String UnEncTGT= sessionKeyBobKdc.concat(socketUsername);
byte[] TGT = encrypt(UnEncTGT, KDCkey);
writer.println(TGT);
}else
{writer.println("Please try again later!");}
}
public static byte[] encrypt(String plainText1, String encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainText1.getBytes("UTF-8"));
}
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
}
the Clientside related code is:
private void recieveTickts() throws IOException, InterruptedException, Exception{
String msg = reader.readLine(); //Lemme check
System.out.println("Server :" + msg);
TimeUnit.SECONDS.sleep(3);
String Sth = reader.readLine(); //please try again
if(Sth.equals("Please try again later!")){
System.out.println(Sth);
System.exit(1);
}else{
ClientTicket = Sth;
String TGT = reader.readLine();
System.out.println("Encrypted key between Client and KDC is " + ClientTicket + " and the TGT is " + TGT);
TimeUnit.SECONDS.sleep(3);
System.out.println("SUCCESSFUL"); }
byte[] b = ClientTicket.getBytes();
System.out.println("b= " + b);
String sessionKeyBobKdc = decrypt(b, Password);
System.out.println(Password + "session key is "+ sessionKeyBobKdc);
}
thanx in advanced guys..
I documented the code to help you out but that is basically a quick and dirty way to never stop listening for data and then also return the received as byte[]. If you don't need to listen at all times just modify this code to stop listening once you received your message
You can make this more dynamic with using parameters for defining the sign that a message is finished etc.
I used a StringBuilder to act as a little buffer. And defined that a message is finished once I receive a LF. Everything else get's put into the "buffer". Additionally I filter out CR since I don't want them in my message.
Socket client;
StringBuilder sb = new StringBuilder();
String result = "";
int c;
try
{
InputStream inputStream = client.getInputStream();
while ( ( c = inputStream.read() ) >= 0 ) // Loop to listen for data until the connection is dead
{
if ( c == 0x0a ) // 0x0a is the hex for a LineFeed ( LF )
{
result = sb.toString(); // put everything you received so far as a final message
sb.delete( 0, sb.length() ); // clear your message "buffer"
}
else if ( c != 0x0d /* <CR> */ ) // Not necessary but it helps keeping the message clear
{
sb.append( (char) c ); // add the received integer as char to the message "buffer"
}
if ( !result.isEmpty() ) // Catch that a message is received
{
log.fine( "received message: " + result ); // just log for tracing
return result.getBytes(StandardCharsets.UTF_8); // return the byte[] of the message with the needed Charset.
}
}
}
catch ( Exception e )
{
log.warning( e.getMessage() );
}
I need your help. My java and python scripts not getting the ame sha-1 value of a string:
hash.py
# -*- coding: utf-8 -*-
import hashlib
username = raw_input('username:')
timestamp = raw_input('timestamp:')
app_id = 'dad'
secret_key = 'dadda'
print 'The hashed string is: ' , hashlib.sha1( username + timestamp + app_id + secret_key ).hexdigest()
hash.java
public static String generateSHA1(String password)
{
String sha1 = "";
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
sha1 = byteToHex(crypt.digest());
}
catch(Exception e)
{
e.printStackTrace();
}
return sha1;
}
private static String byteToHex(final byte[] hash)
{
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
UPDATE:
Assuming password is already the concatenated: username, timestamp, app_id and secret_key
Is there something I missed? I think there is something wrong with my java code re. UTF-8 outputting this: \xe2\x80\x8b but I couldn't figure it out. Any help will be appreciated. Thanks.
Ensure that both inputs use exactly the same format and encoding and try to use HMAC library.
Java:
String key = "2b5ba589b618ff2485f3391e425f86f0f405fd8e";
String data = "Something you want to keep secret!";
byte[] decodedKey = Hex.decodeHex(key.toCharArray());
SecretKeySpec keySpec = new SecretKeySpec(decodedKey, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(keySpec);
byte[] dataBytes = data.getBytes("UTF-8");
byte[] signatureBytes = mac.doFinal(dataBytes);
String signature = new String(Base64.encodeBase64(signatureBytes), "UTF-8");
System.out.println("key = " + key);
System.out.println("data = " + data);
System.out.println("signature = " + signature);
Python:
import hmac
import hashlib
key = "2b5ba589b618ff2485f3391e425f86f0f405fd8e"
data = "Something you want to keep secret!"
decodedKey = key.decode("hex")
hmac = hmac.new(decodedKey, data.encode('UTF-8'), hashlib.sha1)
signature = hmac.digest().encode('base64')
print "key =", key
print "data =", data
print "signature =", signature
Both signature outputs should be the same.
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.