MAC doFinal gives different value for same data - java

I am facing an issue with the following code
public static void main(String[] args) throws NoSuchAlgorithmException {
String key = "test";
SecretKeySpec secretKeySpec = new
SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
Mac mac = Mac.getInstance("HmacSHA512");
try {
if (null != mac) {
mac.init(secretKeySpec);
}
} catch (InvalidKeyException e) {
}
IntStream.range(1, 12).parallel().forEach(d->{
final byte[] bytes = "somestring".getBytes(StandardCharsets.UTF_8);
final byte[] doFinal= mac.doFinal(bytes);
String digest = Base64.getEncoder().encodeToString(doFinal);
LOGGER.info("digest -- {} ", digest);
});
}
Even the string is the same but digests generated using a mac.doFinal is different. This is just a showcase of the issue. I have used the same code in my spring reactive application where mac is a bean and digest generated every time when user make a request which is a concurrent request where also i have seen these weird behavior.

Related

When encoding the password, always return null value

I want to encode my password using an encryption key. but I got a null value, when printing the encoded password. I have attached my code below:
public class FirstJava {
private static final Long ENCRYPTION_KEY = 29190210908917L;
public static String encrypt(String strToEncrypt, byte[] key) {
if (strToEncrypt == null)
return strToEncrypt;
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes()));
} catch (Exception exception) {
System.out.println("ERROR");
}
return null;
}
public static void main(String[] args) {
String password = "12345678";
byte[] arr = String.valueOf(ENCRYPTION_KEY).getBytes();
String passwordEnc = encrypt(password,arr);
System.out.println("passwordEnc============= "+passwordEnc);
}
}
AES only supports key sizes of 16, 24 or 32 bytes. Your key length is 14, add 2 more digits to your key and it will work.
private static final Long ENCRYPTION_KEY = 2919021090891712L; //16 bytes

Decryption result not consistent

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.

SM2Engine can encrypt,but how to add envelop on it?

I can get a public key from a certificate file,and use code below to encrypt a message with sm2engine.
public static String encrypt(String data, PublicKey publicKey)
{
ECPublicKeyParameters localECPublicKeyParameters = null;
if (publicKey instanceof BCECPublicKey)
{
BCECPublicKey localECPublicKey = (BCECPublicKey)publicKey;
ECParameterSpec localECParameterSpec = localECPublicKey.getParameters();
ECDomainParameters localECDomainParameters = new ECDomainParameters(
localECParameterSpec.getCurve(), localECParameterSpec.getG(),
localECParameterSpec.getN());
localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(),
localECDomainParameters);
}
SM2Engine localSM2Engine = new SM2Engine();
localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters,
new SecureRandom()));
byte[] arrayOfByte2;
try
{
arrayOfByte2 = localSM2Engine.processBlock(data.getBytes(), 0, data.getBytes().length);
return new String(Base64.encode(arrayOfByte2));
}
catch (InvalidCipherTextException e)
{
e.printStackTrace();
return null;
}
But it is not enveloped,I do not know how to add an envelop on it,but I know how to add envelop without sm2egine:
public static String encryptMessage(String message, X509Certificate cert) throws Exception {
CMSEnvelopedDataGenerator gen = new CMSEnvelopedDataGenerator();
gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert));
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
CMSTypedData content = new CMSProcessableByteArray(message.getBytes("UTF-8"));
CMSEnvelopedData data = gen.generate(content, encryptor);
String encryptedMessage = new String(Base64.encode(data.getEncoded()));
return encryptedMessage;
}
but now I must use the special algorithm called sm4 as the symmetric algorithm to encrypt the content first,not aes.And then use sm2 to encypt the key of sm4,all as it defined in pkcs7.so how to join these two code fragment?
All special algorithms seem can be implemented by BouncyCastle method,but there is no sm4 in CMSAlgorithm class,but I found a GMObjectIndenrifier.sms4_cbc,and pass it as a parameter,but got a exception:
no such algorithm.
More info:after I parse the certificate from a file, and call certificate.getSigAlgName(),its name is SM3WITHSM2

What is the C# equivalent of the Java SecretKeySpec

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", "~");
}
}

String Encryption/Decryption class that produces different output

I am running a java string encryption/decryption class that i got from the internet! Here is the class with little modification :
public class EncrypterDecrypter
{
Cipher ecipher;
Cipher dcipher;
EncrypterDecrypter(SecretKey key)
{
try {
ecipher = Cipher.getInstance("DES");
dcipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, key);
dcipher.init(Cipher.DECRYPT_MODE, key);
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}
}
public class EncryptionTester
{
public static void main(String[] args)
{
try
{
//Generate a temporary key.
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
//Create Encrypter/Decrypter class
EncrypterDecrypter crypto = new EncrypterDecrypter(key);
//More lines of code to use crypto object
}
catch (Exception e)
{
}
}
}
My problem is that each time i create an new instance of EncrypterDecrypter class i get differents encrypted string yet the string to encrypt is still the same! My mind tells me that the problem would be the SecretKey object which keeps changing each time there is a new instance created, i would like to know how i can make the SecretKey object the same for all instances of Encrypter/Decrypter Class if that be the cause of the problem!
If you used the javax.crypto package, then the encrypt and decryp methods look okay.
Try to generate your key like that:
final SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
final SecretKey key = skf.generateSecret(new DESKeySpec(new byte [] {/*The key*/}));
instance.EncrypterDecrypter(key); //Initialization of your Cipher objects
String encrypted = instance.encrypt("This is a test");
System.out.println(instance.decrypt(encrypted)); //"This is a test"
You should do something in the catches.
Generate SecretKey from a byte array..
byte raw[] = new byte[]{0x001,0x002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002,0X002};
SecretKeySpec spec = new SecretKeySpec(raw, "DES");

Categories

Resources