I have following code written in Java
Mac mac = Mac.getInstance("HmacSHA1");
String secretKey ="sKey";
String content ="Hello";
byte[] secretKeyBArr = secretKey.getBytes();
byte[] contentBArr = content.getBytes();
SecretKeySpec secret_key = new SecretKeySpec(secretKeyBArr,"HmacSHA1");
byte[] secretKeySpecArr = secret_key.getEncoded();
mac.init(secret_key);
byte[] final = mac.doFinal(contentBArr);
I want to make same example in C#. So, I wrote following code
HMACSHA1 hmacsha1 = new HMACSHA1();
string secretKey = "sKey";
string content = "Hello";
byte[] secretKeyBArr = Encoding.UTF8.GetBytes(secretKey);
byte[] contentBArr = Encoding.UTF8.GetBytes(content);
hmacsha1.Key = secretKeyBArr;
byte[] final = hmacsha1.ComputeHash(contentBArr);
Final results are not equal. secretKeyBArr and contentBArr are byte array and their values are same in both example. What is unknown is SecretKeySpec passed to mac.init(). So, what is equivalent same class in C#?
The results are identical, but Java uses signed bytes while C# uses unsigned bytes by default.
Furthermore, SecretKeySpec itself normally does not change the underlying data. You need to e.g. put a DES key specification in a SecretKeyFactory to make sure that the parity bits are set correctly (in the resulting SecretKey). So there is no need for an equivalent as the class itself does very little except wrapping the data.
I'm implementing a credit card payment method form a provider (cardinity) that doesn't provide a .net implementation. I'm looking for similar stuff and end-up writing my own as my google skills seem to be ....
What I need is the base64 string of javax.crypto.mac
I am supporting the following methods:
enum EncryptionMethods
{
None=0,
HMACSHA1,
HMACSHA256,
HMACSHA384,
HMACSHA512,
HMACMD5
}
I have implemented the code you have above, the SecretKeySpec and the Mac the following way (you need System.Security.Cryptography.ProtectedData):
internal class Protected
{
private Byte[] salt = Guid.NewGuid().ToByteArray();
protected byte[] Protect(byte[] data)
{
try
{
return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
return null;
}
}
protected byte[] Unprotect(byte[] data)
{
try
{
return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
return null;
}
}
}
internal class SecretKeySpec:Protected,IDisposable
{
readonly EncryptionMethods _method;
private byte[] _secretKey;
public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
{
_secretKey = Protect(secretKey);
_method = encryptionMethod;
}
public EncryptionMethods Method => _method;
public byte[] SecretKey => Unprotect( _secretKey);
public void Dispose()
{
if (_secretKey == null)
return;
//overwrite array memory
for (int i = 0; i < _secretKey.Length; i++)
{
_secretKey[i] = 0;
}
//set-null
_secretKey = null;
}
~SecretKeySpec()
{
Dispose();
}
}
internal class Mac : Protected,IDisposable
{
byte[] rawHmac;
HMAC mac;
public Mac(SecretKeySpec key, string data)
{
switch (key.Method)
{
case EncryptionMethods.HMACMD5:
mac = new HMACMD5(key.SecretKey);
break;
case EncryptionMethods.HMACSHA512:
mac = new HMACSHA512(key.SecretKey);
break;
case EncryptionMethods.HMACSHA384:
mac = new HMACSHA384(key.SecretKey);
break;
case EncryptionMethods.HMACSHA256:
mac = new HMACSHA256(key.SecretKey);
break;
case EncryptionMethods.HMACSHA1:
mac = new HMACSHA1(key.SecretKey);
break;
default:
throw new NotSupportedException("not supported HMAC");
}
rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));
}
public string AsBase64()
{
return System.Convert.ToBase64String(Unprotect(rawHmac));
}
public void Dispose()
{
if (rawHmac != null)
{
//overwrite memory address
for (int i = 0; i < rawHmac.Length; i++)
{
rawHmac[i] = 0;
}
//release memory now
rawHmac = null;
}
mac?.Dispose();
mac = null;
}
~Mac()
{
Dispose();
}
}
I have implemented this in an OAuthSigner class the following way:
public override string ComputeSignature(string plainTextToEncode, string consumerSecret)
{
var key = PercentEncode(consumerSecret) + "&";
try
{
using (var secretKey = new SecretKeySpec(key.GetBytes(), EncryptionMethods.HMACSHA1))
using (Mac mac = new Mac(secretKey, plainTextToEncode))
{
return mac.AsBase64();
}
}
finally
{
key = null;//free memory, remove sensitive data
}
}
Then, it's not what you ask for but I need a helper method as I am sending my text to a web service that goes like this and I include it as some might copy the code:
public static String PercentEncode(string textToEncode)
{
return string.IsNullOrEmpty(textToEncode)
?""
: UrlEncoder.Default.Encode(Cardinity.ENCODING.GetString(Cardinity.ENCODING.GetBytes(textToEncode)))
.Replace("+", "%20").Replace("*", "%2A")
.Replace("%7E", "~");
}
The class UrlEncoder comes from System.Text.Encodings.Web, you may have to add a reference.
The class named Cardinity implements a "short-cut" to the Encoding that I use for Cardinity
public abstract class Cardinity
{
...
public static String API_BASE = "https://api.cardinity.com";
public static String API_VERSION = "v1";
public static String VERSION = "0.1";
public static String ENCODING_CHARSET = "UTF-8";
public static Encoding ENCODING => Encoding.UTF8;
}
as Java uses string.GetBytes a lot, I have added an extension method for this that I call above in the key.GetBytes(), here is the extension code:
public static byte[] GetBytes(this string sender)=>
Cardinity.ENCODING.GetBytes(sender);
My test method, I have copied the values from Cardinity API passes without any issues.
private OAuthSigner signer;
public HmacOAuthSigner_Test()
{
signer = new HmacOAuthSigner();
}
[TestMethod]
public void Test_HmacOAuthSigner_ComputeSignature_DefaultText()
{
var expects = "PxkffxyQh6jsDNcgJ23GpAxs2y8=";
var test_data = "justsomerandommessage";
var secretkey = "yvp0leodf231ihv9u29uuq6w8o4cat9qz2nkvs55oeu833s621";
var actual = signer.ComputeSignature(test_data, secretkey);
Assert.AreEqual(expects, actual, $"Expecting {test_data} to return {expects} received {actual}");
}
The whole implementation of the HmacOAuthSigner is here, it implements an abstract class with the PercentEncode method in it.
public class HmacOAuthSigner : OAuthSigner
{
public override string ComputeSignature(string signatureBaseString, string consumerSecret)
{
var key = PercentEncode(consumerSecret) + "&";
var secretKey = new SecretKeySpec(key.GetBytes(), EncryptionMethods.HMACSHA1);
using (Mac mac = new Mac(secretKey, signatureBaseString))
{
return mac.AsBase64();
}
}
public override string GetSignatureMethod()
{
return "HMAC-SHA1";
}
}
and the abstract class that I use as a contract for all the implementations:
public abstract class OAuthSigner
{
/// <summary>
/// Signature method used
/// </summary>
/// <returns>a string that tells the implementation method</returns>
public abstract string GetSignatureMethod();
/// <summary>
/// computes the signature that is used with the encryption based on the keys provided by cardinity
/// </summary>
/// <param name="signatureBaseString">The secret string that services as a base</param>
/// <param name="consumerSecret">The consumer key as specified in the API settings</param>
/// <returns>signature string computed by the provided parameters using the signature method</returns>
public abstract string ComputeSignature(String signatureBaseString, String consumerSecret);
/// <summary>
/// Encode a string into a format expected by Cardinity
/// </summary>
/// <param name="textToEncode">The text that is to be encoded</param>
/// <returns>web encoded string ready for using to send to Cardinity</returns>
public static String PercentEncode(string textToEncode)
{
return string.IsNullOrEmpty(textToEncode)
?""
: UrlEncoder.Default.Encode(Cardinity.ENCODING.GetString(Cardinity.ENCODING.GetBytes(textToEncode)))
.Replace("+", "%20").Replace("*", "%2A")
.Replace("%7E", "~");
}
}
Related
I'm trying to port the following Java code to C#, but so far it still says that the signature is invalid.
private static String generateSignStr(Map<String, String> params, String key) {
StringBuilder sb = new StringBuilder();
params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(entry.getKey()).append('=');
sb.append(entry.getValue());
});
sb.append('&').append("api_secret")
.append('=').append(key);
return sb.toString();
}
private static String sign(String target) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
log.error("Fail to get MD5 instance");
return null;
}
md.update(target.getBytes());
byte[] dg = md.digest();
StringBuilder output = new StringBuilder(dg.length * 2);
for (byte dgByte : dg) {
int current = dgByte & 0xff;
if (current < 16) {
output.append("0");
}
output.append(Integer.toString(current, 16));
}
return output.toString();
}
private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
var sb = new StringBuilder();
var queryParameterString = string.Join("&",
query.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value.ToString()))
.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString())}"));
sb.Append(queryParameterString);
if (sb.Length > 0)
{
sb.Append('&');
}
sb.Append("api_secret=").Append(apiSecret);
return sb.ToString();
}
private static string Sign(string source)
{
using var md5 = MD5.Create();
var sourceBytes = Encoding.UTF8.GetBytes(source);
var hash = md5.ComputeHash(sourceBytes);
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}
Edit:
This fixed it. However, it would be nice if someone knows a way to lexicographically sort the dictionary inside that method just like the Java code.
var #params = new Dictionary<string, object>
{
{ "api_key", _apiKey },
{ "req_time", now },
{ "op", "sub.personal" }
};
var javaSorted = #params.OrderBy(item => item.Key, StringComparer.Ordinal)
.ToDictionary(i => i.Key, i => i.Value);
var signature = Sign(GenerateSign(javaSorted, _apiSecret));
In GenerateSign method you can just create instance of SortedDictionary based on dictionary passed as parameter:
private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
var sortedDict = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
// rest of the method
}
Or you can do even better (note the important change from Dictionary to IDictionary):
private static string GenerateSign(IDictionary<string, object> query, string apiSecret)
{
query = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
// rest of the method
}
Recently i tried to get value of secret key used to HMAC but i'm unable to hook into constructor. Here is my frida code:
var secretKey = Java.use("javax.crypto.spec.SecretKeySpec")
var mac = Java.use("javax.crypto.Mac")
mac.init.overload('java.security.Key').implementation = function (bytes) {
console.log("Mac init..")
return this.init(bytes)
}
secretKey.$init.overload('[B', 'java.lang.String').implementation = function (keyBytes, algo) {
console.log("SecretKey (" + algo + ") -> ")
return this.$init(keyBytes, algo)
}
secretKey.$init.overload('[B', 'int', 'int', 'java.lang.String').implementation = function (keyBytes, i, i2, algo) {
console.log("SecretKey (" + algo + ") -> ")
return this.$init(keyBytes, i, i2, algo)
}
But any of those hooks works. I don't know what can be a reason. I checked that this app using Enum to create Mac instance, and it's probably executed during runtime. That's how code looks:
public static void m26694i(Type type, String str) throws Exception {
synchronized (f21525a) {
if (f21526b == null) {
f21526b = type.getMacEnc(str);
}
}
}
#2
public enum Type {
KEY {
#Override
/* renamed from: getMac */
public Mac getMacEnc(String str) throws Exception {
return HmacUtil.m26681a(str);
}
},
FILE {
#Override
/* renamed from: getMac */
public Mac getMacEnc(String str) throws Exception {
Properties properties = new Properties();
InputStream inputStream = null;
try {
inputStream = MACManager.class.getResourceAsStream(str);
if (inputStream != null) {
properties.load(inputStream);
return HmacUtil.m26681a((String) properties.elements().nextElement());
}
throw new FileNotFoundException(str);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
};
/* renamed from: getMac */
public abstract Mac getMacEnc(String str) throws Exception;
}
#3
public static Mac m26681a(String str) throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec secretKeySpec = new SecretKeySpec(str.getBytes(), "HmacSHA1");
Mac instance = Mac.getInstance("HmacSHA1");
instance.init(secretKeySpec);
return instance;
}
Any ideas how to hook into SecretKeySpec constructor?
I have two instances of my service that does the encryption and decryption running in cloud. The decryption fails sometimes with 'decryption failed' error. I guess this is because each instance has its own Aead instance. How can i solve this issue?
public class Utils {
private static final Logger log = LoggerFactory.getLogger(Utils.class);
private Aead aead;
private static Utils utils;
private Utils() {
try {
AeadConfig.register();
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
aead = AeadFactory.getPrimitive(keysetHandle);
} catch (GeneralSecurityException e) {
log.error(String.format("Error occured: %s",e.getMessage())).log();
}
}
public static Utils getInstance() {
if(null == utils) {
utils = new Utils();
}
return utils;
}
public String encrypt(String text) throws GeneralSecurityException, UnsupportedEncodingException {
byte[] plainText = text.getBytes("ISO-8859-1");
byte[] additionalData = null;
byte[] cipherText = aead.encrypt(plainText,additionalData);
String output = Base64.getEncoder().encodeToString(cipherText);
return output;
}
public String decrypt(String text) throws GeneralSecurityException, UnsupportedEncodingException {
byte[] cipherText = Base64.getDecoder().decode(text);
byte[] additionalData = null;
byte[] decipheredData = aead.decrypt(cipherText,additionalData);
String output = new String(decipheredData,"ISO-8859-1");
return output;
}
#Test
public void encrypt() throws IOException, GeneralSecurityException {
String encryptedText = cryptographicUtils.encrypt("Hello World");
assertThat(encryptedText, Matchers.notNullValue());
}
#Test
public void decrypt() throws IOException, GeneralSecurityException {
String encryptedText = cryptographicUtils.encrypt("Hello 123456");
String decrypedText = cryptographicUtils.decrypt(encryptedText);
assertThat(decrypedText, Matchers.is("Hello 123456"));
}
I am getting consistent result if only one instance is running...
I will have to use the same keyset to encrypt and decrypt. I am able to resolve the issue by storing the keyset in a physical location and use it to create Aead instance. With this change all instances of my service able to decrypt string successfully
Looks like a thread safety issue. Try making the getInstance synchronized. Also, protect access to private Aead aead
Multiple threads can be altering the state of aead if you're not careful.
Consider a queue to do your work, or synchronize access to what is interacting with aead.
I am a newbie to Encryption algos and C# code.
I am trying to get some data which is being encrypted decrypted using RSA algo in C# code. Below is C# code. I can't find any similar library in Java, can you help me with getting a java equivalent for the same.
public byte[] Encrypt(byte[] data, string keyContainerName, bool doOaepPadding = false)
{
return DoCryptoTransformation(data, true, keyContainerName, doOaepPadding);
}
public byte[] Decrypt(byte[] data, string keyContainerName, bool doOaepPadding = false)
{
return DoCryptoTransformation(data, false, keyContainerName, doOaepPadding);
}
private byte[] DoCryptoTransformation(byte[] data, bool encryption, string keyContainerName,
bool doOaepPadding = false)
{
try
{
byte[] processedData;
RSACryptoServiceProvider.UseMachineKeyStore = true;
CspParameters cspParams = CreateCspParameters(keyContainerName);
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams))
{
processedData = encryption ? rsa.Encrypt(data, doOaepPadding) : rsa.Decrypt(data, doOaepPadding);
}
return processedData;
}
catch (CryptographicException ex)
{
string message = string.Format("RsaCryptoProvider: Failed to {0} data.", encryption ? "encrypt" : "decrypt");
Logger.Error("{0}. Exception details: {1}", message, ex);
throw new CryptoProviderException(message, ex);
}
}
private static CspParameters CreateCspParameters(string keyContainerName)
{
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = keyContainerName;
cspParams.Flags |= CspProviderFlags.UseExistingKey;
cspParams.Flags |= CspProviderFlags.UseMachineKeyStore;
return cspParams;
}
}
The methods used which I want to redefine implementation for in java:
var previousKey = _encryptionService.RsaDecrypt(Key, CoreSettings.Instance.KeyContainerName);
Thanks for help!
I am trying to take in a string(message) and encrypt it then send it to the firebase database. Then when the app loads the data should be displayed to the user un-encrypted. So the encrypted string lies on the server/database.
Im getting nul pointer exception because it seems when you use snapshot it calls the default message constructor which does not contain the text which i called for.Essentially datasnapshot calls the default constructor which does not let me call the attributes of the object in the actual constructor. Is there anyway around this ?
Once the app request the Method that will request the messages from the database.The App crashes. I try to de-crypt the messages from a String of bytes before i display them... The messages however will no display and the app crashes
Here's a link to a screenshot
http://ibb.co/hqSJFv
This is the method that gets called when the sign in is successful and request the messages from the database and displays them(this worked until i messed with decrypting) I want to read from DB and decrypt then display the messages
//READ DECRYPT AND DISPLAY THE MESSAGES
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//called when new message is inserted into the list also
//triggered for every child message
EncryptoMessage encryptoMessage =
dataSnapshot.getValue(EncryptoMessage.class);
//get the text from the encryptomessage object store as string
String encryptedString = encryptoMessage.getText(); //value of
//encryptedString ends up empty
byte [] bytes = encryptedString.getBytes();
String str = null;
try {
str = new EncryptoMessage().decrypt(bytes);
} catch (Exception e) {
e.printStackTrace();
}
EncryptoMessage decryptedMessage = new EncryptoMessage();
decryptedMessage.setText(str);
mMessageAdapter.add(decryptedMessage);
}
This is the message from user input encrypted and stored as a string in DB. I coverted the btye[] to a string
//SEND CLICKED
// Send button sends a message and clears the EditText && Write to the
//Database
mSendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// TODO: Send messages on click
//get the message the user enters
String messageText = mMessageEditText.getText().toString();
byte[] codedtext = new byte[0];
try {
//take that message and encrypt it
//codedtext now equal to byte array of messageText
codedtext = new EncryptoMessage().encrypt(messageText);
} catch (Exception e) {
e.printStackTrace();
}
//change the encrypted bytes into a string and send to the
//database
String str = new String(codedtext, StandardCharsets.UTF_8);
//Create an EncryptoMessage object for the message the user
//enters
EncryptoMessage encryptoMessage = new EncryptoMessage(str,
mUsername, null);
//add the new message object to the EncryptoChat database
mMsgDatabaseReference.push().setValue(encryptoMessage);
// Clear input box
mMessageEditText.setText("");
}
});
This is Encrypted message object class(with encryption)
package com.google.firebase.david.encryptochat; //firebase API
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//**************************************
//THis class is our message object
//*****************************************
public class EncryptoMessage {
private String text;
private String name;
private String photoUrl;
public EncryptoMessage() {
// Default constructor required for calls to
//DataSnapshot.getValue(User.class)
}
// The encrypto message constructor to create the object
public EncryptoMessage(String text, String name, String photoUrl) {
this.text = text;
this.name = name;
this.photoUrl = photoUrl;
}
//Copy constuctor
/* public EncryptoMessage(EncryptoMessage EncryptoMessageCopy){
this(EncryptoMessageCopy.getText(), EncryptoMessageCopy.getName());
}*/
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhotoUrl() {
return photoUrl;
}
public void setPhotoUrl(String photoUrl) {
this.photoUrl = photoUrl;
}
//Encryption of the messages
public byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return cipherText;
}
//Decrypt
public String decrypt(byte[] message) throws Exception {
//get the bytes if the string passing in
//byte [] bytes = message.getBytes();
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher =
Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
}
This is the error i get at runtime just when i sign in and display messages it crashes also
FATAL EXCEPTION: main
Process: com.google.firebase.david.encryptochat, PID: 8733
java.lang.NullPointerException: Attempt to invoke virtual method 'byte[] java.lang.String.getBytes()' on a null object reference
at com.google.firebase.david.encryptochat.MainActivity$4.onChildAdded(MainActivity.java:238)