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.
Related
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 have the following JAVA and I am trying to convert into C# using ChilKat (or BouncyCastle) I have a working version in ChilKat, but not sure how to validate Here is the JAVA code:
private SecretKey symmKey = null;
public String encrypt(String strToEncrypt) throws Exception
{
String symmEncryptMode = "DESede";
String encString= null;
KeyGenerator keyGen = KeyGenerator.getInstance(symmEncryptMode);
symmKey = keyGen.generateKey();
byte dataToEncrypt[] = strToEncrypt.getBytes();
Cipher symmCipher = Cipher.getInstance(symmEncryptMode);
symmCipher.init(Cipher.ENCRYPT_MODE, symmKey);
byte[] encrypted = symmCipher.doFinal(dataToEncrypt);
encString= new String(Base64.encode(encrypted));
encString = URLEncoder.encode(encString, "UTF-8");
return(encString);
} //end method create Signature
Here is what I have so far (It returns a value, but I don't know how to validate as this is one of three steps of my encyption process - step 3 works, 1 and 2 are suspect, so I figured I'd ask one at a time...) This uses ChilKat and it returns a value, but I am not sure if it is correct:
private static string EncryptStringSymmetric(string data2Encrypt, ref string passKey)
{
//Init Encryptor
Crypt2 encryptor = new Crypt2();
bool success = encryptor.UnlockComponent("Anything for 30 - day trial");
if (success != true)
{ throw (new Exception("Crypt component unlock failed")); }
//Encryptor Settings
encryptor.CryptAlgorithm = "3des";
encryptor.KeyLength = 192;
encryptor.EncodingMode = "base64";
encryptor.PaddingScheme = 0;
encryptor.Charset = "utf-8";
encryptor.CipherMode = "ecb";
encryptor.RandomizeKey();
passKey = encryptor.GetEncodedKey("base64");
string eStr;
//byte[] bytesToEncrypt = Encoding.ASCII.GetBytes(data2Encrypt);
//eStr = encryptor.EncryptBytesENC(bytesToEncrypt);//??
eStr = encryptor.EncryptStringENC(data2Encrypt);
return eStr;
}
I am trying to decrypt an AES encrypted string from Java, in C#. When I decrypt, it returns gibberish and does not match the original plain text, which was encrypted via Java code. Pls guide me on what is going wrong here.
Attached the Java code for encryption and the C# code for decryption. Pls let me know if you need more details.
I tried AesCryptoServiceProvider as well and it did not work either. You can see the code tried in the commented code in C#.
Pls note that I can make changes on my C# code only to match the Java code and can not make any edits to Java side.
Java code for Encryption:
/** encrypt cipher */
private static final Cipher ENCRYPT_CIPHER = generateCipher(Cipher.ENCRYPT_MODE);
private static String ENCRYPT_KEY = "key";
/**
* #param val
* #return encrypted value
* #throws Exception
*/
public String encrypt(final String val) throws Exception {
return new String(Base64.encodeBase64(ENCRYPT_CIPHER.doFinal(val.getBytes()), true)).toString();
}
/**
* #param encrypt
* #return cipher
*/
protected static Cipher generateCipher(final int encrypt) {
try {
final Cipher cipher = Cipher.getInstance("AES");
cipher.init(encrypt, SecretKeyFactory.getInstance("AES").generateSecret(new IBMAESKeySpec(Base64.decodeBase64(ENCRYPT_KEY.getBytes()))));
return cipher;
} catch (final Exception e) {
return null;
}
}
C# code for decryption:
private static String ENCRYPT_KEY = "key";
public String decodeString (String encodedStr)
{
/*using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
aesCryptoProvider.BlockSize = 128;
aesCryptoProvider.KeySize = 256;
aesCryptoProvider.Key = Convert.FromBase64String(ENCRYPT_KEY.ToString());
aesCryptoProvider.Padding = PaddingMode.Zeros;
aesCryptoProvider.Mode = CipherMode.ECB;
using (var decryptor = aesCryptoProvider.CreateDecryptor())
using (var memoryStream = new MemoryStream(Convert.FromBase64String(encodedStr)))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream, Encoding.UTF8))
{
decodedStr = streamReader.ReadToEnd();
}
}
*/
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Convert.FromBase64String(ENCRYPT_KEY.ToString()); ;
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
aesAlg.Mode = CipherMode.ECB;
aesAlg.Padding = PaddingMode.Zeros;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor();
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(encodedStr)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
decodedStr = srDecrypt.ReadToEnd();
}
}
}
}
}
This is just a quick answer. Haven't done a ton of research into it, but have you checked to see if the endian-ness matches? It looks like C# (.NET) is little-endian, but the JVM is big-endian. I'm not sure if it swaps it for network transmission, however (then it would just match the hardware). Just an idea. If I find anything additional, I'll update my answer.
I am trying to encrypt a string using the RSA public key encryption. I have a public key which is:
-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7yyFgPV6Fk8JWngl3MIz\n1U2DMwKPNCRkeQ79g/qwKL0LP5aVkQUUxqYhX0mmQMWnT5t+7Hoc6UUxyjGSkI9o\nv0Q3oTSVtByIQDdySXZWihzjVjn3h98chevZuNNkJ4GNADHj5K/7LOWLpKSQJ2Hj\nIFdVrcKSjy4kiP/UMHgsfq0GWQAtGv8ebcybWuEf8rzTMndxmI96Nmz5PgPK7K75\nXbMgJlOMoMlXDsgmghpGzH8p10r/qHGlYi/COa4PZ7Pvbveg1BoH5LPy/8mLZ+Oa\n9owl10yBIoh9/H5KnijZ0Uq8MH0QdgQXekLC9sRh3uGTe69IQrGXzSv7tHe5fgv6\nnwIDAQAB\n-----END
PUBLIC KEY-----
I am using following code to encrypt it :
private String generateRSAEncryptedText(String publicKey) {
String baseCredentials = email + "---" + password;
try {
return encryptRSA(context, baseCredentials, publicKey);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String getPublicKeyStringFromPemFormat(String PEMString, boolean isFilePath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
BufferedReader pemReader = null;
if (isFilePath) {
pemReader = new BufferedReader(new InputStreamReader(new FileInputStream(PEMString)));
} else {
pemReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(PEMString.getBytes("UTF-8"))));
}
StringBuffer content = new StringBuffer();
String line = null;
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----BEGIN PUBLIC KEY-----") != -1) {
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----END PUBLIC KEY") != -1) {
break;
}
content.append(line.trim());
}
break;
}
}
if (line == null) {
throw new IOException("PUBLIC KEY" + " not found");
}
Log.i("PUBLIC KEY: ", "PEM content = : " + content.toString());
return content.toString();
}
public String encryptRSA(Context mContext, String message, String publicKeyString) throws Exception {
String keyString = getPublicKeyStringFromPemFormat(publicKeyString, false);
// converts the String to a PublicKey instance
byte[] keyBytes = Base64.decode(keyString.getBytes("utf-8"), Base64.NO_WRAP);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(spec);
// decrypts the message
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
dectyptedText = cipher.doFinal(Base64.decode(message.getBytes("utf-8"), Base64.NO_WRAP));
return Base64.encodeToString(dectyptedText, Base64.NO_WRAP);
}
Using this, i am able to generate an encrypted text but the PHP server is not able to decrypt my encrypted text.
Server is working correctly as i am able to the whole exercise using a python script but it is not working for android.
Any idea what i might be doing wrong.. all help is appreciated.
Some other pointer:
I get the public key as a string from the server and it can be changed at any time so cant save it in a cert file.
I am using android.util.Base64 class for encoding.
Let me know if you need any other information.
Working Python Code:
import requests
import base64
def try_api():
API_ENDPOINT = 'https://example.com/login.php'
PUBLIC_KEY = None
###get_pulic_key###
payload = {'requestType':'getPubkey'}
r = requests.post(API_ENDPOINT, data=payload, verify=False)
mycookies = dict (r.cookies)
#print mycookies
res = r.json()
PUBLIC_KEY = res[1]
print "PublicKey\n", PUBLIC_KEY
###get_token###
credential = prepare_credentials('abc#gmail.com', 'abctest', PUBLIC_KEY)
print "Credential\n", credential
cred_payload = {'requestType':'credentialLogin', 'credential':credential}
r = requests.post(API_ENDPOINT, data=cred_payload, cookies=mycookies, verify=False)
#print r.text
status, token =r.json()
def prepare_credentials(username, password, public_key):
"""
Given username and password prepare the credentials
"""
basecred = "%s---%s" % (username, password)
#print "basecred\n", basecred
basecred64 = base64.b64encode(basecred)
#print "basecred64\n", basecred64
basecred64encrypted64 = encrypt_RSA(public_key, basecred64)
#print "basecred64encrypted64\n", basecred64encrypted64
return basecred64encrypted64
def encrypt_RSA(public_key, message):
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
rsakey = RSA.importKey(public_key)
rsakey = PKCS1_v1_5.new(rsakey)
encrypted = rsakey.encrypt(message)
return base64.b64encode(encrypted)
if __name__=='__main__':
try_api()
in PHP I have the following function:
base64_encode(hash_hmac('sha256', $data, $secret, false));
I'm trying to create a function in Java that will give the same result for the same "data" and "secret" parameters.
I tried to use this function:
public static String base64sha256(String data, String secret) {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] res = sha256_HMAC.doFinal(data.getBytes());
return Base64.encodeToString(res, Base64.NO_WRAP);
}
But I get different results for the same input
Update: This function works. Enjoy.
public static String base64sha256(String data, String secret) {
String hash = null;
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] res = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
hash = getHex(res);
hash = Base64.encodeToString(hash.getBytes("UTF-8"), Base64.NO_WRAP);
} catch (Exception e){}
return hash;
}
static final String HEXES = "0123456789abcdef";
public static String getHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
final StringBuilder hex = new StringBuilder( 2 * raw.length );
for ( final byte b : raw ) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
The output of the php function are lowercase hex digits when the fourth parameter is false. Your second java version however produces uppercase hex digits. Either correct the case difference or you could change the fourth parameter of hash_hmac to true and it will probably match with your first Java version.
If trying to match output of drupal_hmac_base64 with Java 8, you can use the following code:
final String ALGORITHM = "HmacSHA256";
Mac mac = Mac.getInstance(ALGORITHM);
SecretKeySpec secret = new SecretKeySpec(authorizationKey.getBytes(), ALGORITHM);
mac.init(secret);
byte[] digest = mac.doFinal(body.getBytes());
hash = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
return signature.equals(hash);
Note that drupal returns a hash using raw binary data (3rd parameter TRUE). Also, base64 encoding in PHP matches the URL and Filename safe base64 encoder in Java https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html#url.
For someone who might be facing a slight change (not working) in Java result compared to PHP, my issue was in returning the hash from HmacSHA256 as String, while you should return it and pass to Hex as byte[].
Here are the working methods to simulate PHP's hash_hmac()
public String hashValue(String message) {
byte[] hash = toHmacSHA256(message);
String hashHexed = toHex(hash);
return hashHexed;
}
private String toHex(byte[] value) {
String hexed = String.format("%040x", new BigInteger(1, value));
return hexed;
}
private byte[] toHmacSHA256(String value) {
byte[] hash = null;
try {
SecretKey secretKey = new SecretKeySpec(PRIVATE_KEY.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
hash = mac.doFinal(value.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return hash;
}