How to use Java encrypt Json by HmacSHA256 - java

I have such a Json :
{
"idcardno":"510525198803154232",
"name":"丁品"
}
If I use HmacSHA256 encrypt it with key
252c04cdb0d047f8ab9a1eb49b1db1686e321104756ff792779a4d40d94f0dfd70a8b9ffa6d6d930e57d0e7206d26d13
Then I should get such result:
cd1fe72697a832d57198b8c0d00289309dffe6f05750aa2a145f9359e41f1843
I need to encrypt this json object in java environemnt, so I use below java code to achieve this:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONObject;
public class EncryptUtil {
// Charset to use when encrypting a string.
private static final String UTF_8 = "UTF-8";
// Encrypt key
private static final String APP_SECRET = "252c04cdb0d047f8ab9a1eb49b1db1686e321104756ff792779a4d40d94f0dfd70a8b9ffa6d6d930e57d0e7206d";
public static void main(String[] args) throws Exception {
//build json object
JSONObject object = new JSONObject();
object.put("idcardno", "510525198803154232");
object.put("name", "丁品");
String result = enCode(object.toString());
//print encrpted result
System.out.println(result);
}
public static String enCode(String orginalMsg) throws Exception {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(APP_SECRET.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = Base64.encodeBase64String(sha256_HMAC.doFinal(orginalMsg.getBytes(UTF_8)));
return hash;
}
catch (Exception e){
throw new Exception(e);
}
}
}
But the encypted result is :
i2hF1WyxmS9MctkFLxBM2mgXeicraH9DhR9P9JySDFk=
How can get the correct result by Java code?

Related

Difference between these two HMAC excamples?

I have to calc HMAC signature to compare to the one from a message.
I come to two different results and do not understand the difference nor which one is correct.
I have even tested different online HMAC validators and get both version from different web sites.
the message: 7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true
the key: 44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056
result 1: /b1O7eDkBtlZ3I1xH+qMl/I1aRBDel8Y4sbLZXnDKEI=
result 2: coqCmt/IZ4E3CzPvMY8zTjQVL5hYJUiBRg8UU+iCWo0=
I guess that result 2 is the correct one.
Java code calculating the first result:
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HMAC {
static public byte[] calcHmacSha256(byte[] secretKey, byte[] message) {
byte[] hmacSha256 = null;
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
mac.init(secretKeySpec);
hmacSha256 = mac.doFinal(message);
} catch (Exception e) {
throw new RuntimeException("Failed to calculate hmac-sha256", e);
}
return hmacSha256;
}
public static void main(String[] args) {
String message = "7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true";
String key = "44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056";
try {
byte[] hmacSha256 = HMAC.calcHmacSha256(key.getBytes("UTF-8"), message.getBytes("UTF-8"));
//Output of HEX
System.out.println(String.format("Hex: %064x", new BigInteger(1, hmacSha256)));
System.out.println("Base64: " + Base64.getEncoder().encodeToString(hmacSha256));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
Java code comming to the second result
import java.nio.charset.StandardCharsets;
import java.security.SignatureException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
public class HMACValidator {
public static void main(String[] args) throws IllegalArgumentException, SignatureException {
String message = "7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true";
String key = "44782def547aaa06c910c43932b1eb0c71fc68d9d0c057550c48ec2acf6ba056";
HMACValidator demo = new HMACValidator();
String result = demo.calculateHMAC(message, key);
System.out.println(result);
}
public static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
// To calculate the HMAC SHA-256
public String calculateHMAC(String data, String key) throws IllegalArgumentException, SignatureException {
try {
if (data == null || key == null) {
throw new IllegalArgumentException();
}
byte[] rawKey = Hex.decodeHex(key.toCharArray());
// Create an hmac_sha256 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(rawKey, HMAC_SHA256_ALGORITHM);
// Get an hmac_sha256 Mac instance and initialize with the signing ey
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
// Base64-encode the hmac
return new String(Base64.encodeBase64(rawHmac));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Missing data or key.");
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
}
}
Could someone please point me in the right direction?
Websites
https://www.devglan.com/online-tools/hmac-sha256-online is doing it "wrong"
https://cryptii.com/ is doing it "correct"

Trying to generate WM_SEC.AUTH_SIGNATURE (Walmart.io)

Has anyone here successfully generated the Walmart.io API signature key?
I have tried to wrap my head around the java example but I am not having any luck of it as I have no Java experience.
I saw that there is a Python example here on StackOverFlow which would be interesting too, but it didn't work for me.
The result I'm getting with Walmart Java code:
consumerId: 36e010ef-0026-4713-9365-231323116afd
intimestamp: 1665872674888
Signature: null
My code:
package com.walmart.platform.common;
import java.io.ObjectStreamException;
import java.security.KeyRep;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.codec.binary.Base64;
public class SignatureGenerator {
public static void main(String[] args) {
SignatureGenerator generator = new SignatureGenerator();
String consumerId = "36e010ef-0026-4713-9365-231323116afd";
String priviateKeyVersion = "2";
String key = "jUiewb1+QbHl7ls+LoBO...O4j2NVjC2If4Z/r5FrykDVcO+nxb8G95X+zl";
long intimestamp = System.currentTimeMillis();
System.out.println("consumerId: " + consumerId);
System.out.println("intimestamp: " + intimestamp);
Map<String, String> map = new HashMap<>();
map.put("WM_CONSUMER.ID", consumerId);
map.put("WM_CONSUMER.INTIMESTAMP", Long.toString(intimestamp));
map.put("WM_SEC.KEY_VERSION", priviateKeyVersion);
String[] array = canonicalize(map);
String data = null;
try {
data = generator.generateSignature(key, array[1]);
} catch(Exception e) { }
System.out.println("Signature: " + data);
}
public String generateSignature(String key, String stringToSign) throws Exception {
Signature signatureInstance = Signature.getInstance("SHA256WithRSA");
ServiceKeyRep keyRep = new ServiceKeyRep(KeyRep.Type.PRIVATE, "RSA", "PKCS#8", Base64.decodeBase64(key));
PrivateKey resolvedPrivateKey = (PrivateKey) keyRep.readResolve();
signatureInstance.initSign(resolvedPrivateKey);
byte[] bytesToSign = stringToSign.getBytes("UTF-8");
signatureInstance.update(bytesToSign);
byte[] signatureBytes = signatureInstance.sign();
String signatureString = Base64.encodeBase64String(signatureBytes);
return signatureString;
}
protected static String[] canonicalize(Map<String, String> headersToSign) {
StringBuffer canonicalizedStrBuffer=new StringBuffer();
StringBuffer parameterNamesBuffer=new StringBuffer();
Set<String> keySet=headersToSign.keySet();
// Create sorted key set to enforce order on the key names
SortedSet<String> sortedKeySet=new TreeSet<String>(keySet);
for (String key :sortedKeySet) {
Object val=headersToSign.get(key);
parameterNamesBuffer.append(key.trim()).append(";");
canonicalizedStrBuffer.append(val.toString().trim()).append("\n");
}
return new String[] {parameterNamesBuffer.toString(), canonicalizedStrBuffer.toString()};
}
class ServiceKeyRep extends KeyRep {
private static final long serialVersionUID = -7213340660431987616L;
public ServiceKeyRep(Type type, String algorithm, String format, byte[] encoded) {
super(type, algorithm, format, encoded);
}
protected Object readResolve() throws ObjectStreamException {
return super.readResolve();
}
}
}
Looks like private key you are using is not valid. Could you please regenerate public/private key pair and input valid private key. It will generate signature
Please follow the instructions listed here
https://walmart.io/docs/affiliate/quick-start-guide
https://walmart.io/key-tutorial
Regards,
Firdos
IO Support

JNCryptor: Why does my call to decryptData not produce the correct result?

I need use the JNCryptor library in a project, so I am first trying to get a very simple example of encryption/decryption to work. My program just encrypts a short string, then decrypts it and displays the result. The problem is that I don't get the original text back. Here is the output when it runs:
C:\Java\JNCryptor_Test>java JNCryptorTest
Encrypted text: [B#2cfb4a64
Encrypted text back to plain text: [B#5474c6c
Can anyone tell me what I am doing wrong?
Here is the source code for my class JNCryptorTest:
import org.cryptonode.jncryptor.JNCryptor;
import org.cryptonode.jncryptor.AES256JNCryptor;
import org.cryptonode.jncryptor.CryptorException;
public class JNCryptorTest
{
private static String plaintext = "Hello, World!";
private static String password = "secretsquirrel";
public static void main(String[] args)
{
AllowAes256BitKeys.fixKeyLength();
byte[] encrypted = encrypt(plaintext);
System.out.println("Encrypted text: " + encrypted.toString());
String decrypted = decrypt(encrypted);
System.out.println("Encrypted text back to plain text: " + decrypted);
}
private static byte[] encrypt(String s)
{
JNCryptor cryptor = new AES256JNCryptor();
try
{
return cryptor.encryptData(s.getBytes(), password.toCharArray());
}
catch (CryptorException e)
{
// Something went wrong
e.printStackTrace();
return null;
}
}
private static String decrypt(byte[] msg)
{
JNCryptor cryptor = new AES256JNCryptor();
try
{
return (cryptor.decryptData(msg,
password.toCharArray())).toString();
}
catch (CryptorException e)
{
// Something went wrong
e.printStackTrace();
return null;
}
}
}
Also, I had to create the class AllowAes256BitKeys to allow 256-bit keys. It was recommended to install the "unlimited strength jurisdiction files" into the JVM, but that is unacceptable at our site, so I found a way to do it without that (see Java Security: Illegal key size or default parameters?).
Here is the source code for my class AllowAes256BitKeys:
import javax.crypto.Cipher;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
public class AllowAes256BitKeys
{
// From https://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters
public static void fixKeyLength()
{
String errorString =
"Unable to manually override key-length permissions.";
int newMaxKeyLength;
try
{
if ((newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES")) < 256)
{
Class<?> c =
Class.forName("javax.crypto.CryptoAllPermissionCollection");
Constructor con = c.getDeclaredConstructor();
con.setAccessible(true);
Object allPermissionCollection = con.newInstance();
Field f = c.getDeclaredField("all_allowed");
f.setAccessible(true);
f.setBoolean(allPermissionCollection, true);
c = Class.forName("javax.crypto.CryptoPermissions");
con = c.getDeclaredConstructor();
con.setAccessible(true);
Object allPermissions = con.newInstance();
f = c.getDeclaredField("perms");
f.setAccessible(true);
// Warnings suppressed because CryptoPermissions uses a raw Map
#SuppressWarnings({"unchecked"})
Object junk = // Only need this so we can use #SuppressWarnings
((Map) f.get(allPermissions)).put("*", allPermissionCollection);
// ((Map) f.get(allPermissions)).put("*", allPermissionCollection);
c = Class.forName("javax.crypto.JceSecurityManager");
f = c.getDeclaredField("defaultPolicy");
f.setAccessible(true);
Field mf = Field.class.getDeclaredField("modifiers");
mf.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(null, allPermissions);
newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
}
}
catch (Exception e)
{
throw new RuntimeException(errorString, e);
}
if (newMaxKeyLength < 256)
throw new RuntimeException(errorString); // hack failed
}
}
Thanks
However, I was able to resolve this myself with some help from a more experienced Java developer. It turns out that there was no issue with JNCryptor; the problem is that I was converting the decrypted result (a byte array) into a String incorrectly. The toString method is not the correct way to do that; you have to create a new String object and pass the byte array to its constructor.
So I changed this line in my decrypt method
return (cryptor.decryptData(msg, password.toCharArray())).toString();
to this
return (new String(cryptor.decryptData(msg, password.toCharArray())));
and then I got the correct result:
Encrypted text: [B#2cfb4a64
Encrypted text back to plain text: Hello, World!
So actually, JNCryptor was working correctly the whole time- It was my code which displayed the result that was the problem.

Python DES Encryption

I have a project where our provider has their own DES Encryption logic however I am using django as my backend. I can run the java using subprocess but I'm actually planning to convert that java code to python..
Here is my code:
import java.io.*;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class PHPDESEncrypt {
String key;
public PHPDESEncrypt() {
}
public PHPDESEncrypt(String key) {
this.key = key;
}
public byte[] desEncrypt(byte[] plainText) throws Exception {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key, sr);
byte data[] = plainText;
byte encryptedData[] = cipher.doFinal(data);
return encryptedData;
}
public String encrypt(String input) throws Exception {
return base64Encode(desEncrypt(input.getBytes())).replaceAll("\\s*", "");
}
public String base64Encode(byte[] s) {
if (s == null) return null;
BASE64Encoder b = new BASE64Encoder();
return b.encode(s);
}
public static void main(String args[]) {
try {
PHPDESEncrypt d = new PHPDESEncrypt(args[0]);
String p=d.encrypt(args[1]);
System.out.println(p);
}
catch (Exception e) {
e.printStackTrace();
}
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
Any suggestions on how to convert this? Like a library or an online tool? Or it would be better if there is someone good enough who can easily convert this
I run it in java like:
java PHPDESEncrypt "3r108w5A" "cagent=sample/\\\\/loginname=sample/\\\\/password=password/\\\\/method=ca/\\\\/actype=0/\\\\/cur=USD"
The result should be:
lt5tEqnzRCx67vJ8j3Ap5zIVZYkT01Ho+irM1NPdReJqwyrP9vlypDXvExx9sCOEJPcPCET0aPpKFkW1punRxP/uD8IFM1j4umwLuJpxPQTOdwJsbuuRmhmkFml6l3OV
use pyDes with python3, here is the demo code:
def des_ecb_encode(source, key):
des_obj = des(key, ECB, IV=None, pad=None, padmode=PAD_PKCS5)
des_result = des_obj.encrypt(source)
return base64.encodestring(des_result)
if __name__ == '__main__':
src = b'cagent=81288128/\\\\/method=tc'
key = b'12341234'
encrypted = des_ecb_encode(src, key)
print('encrypted: ', encrypted)
and it will print
encrypted: b'IGcOAYEQN88F1NFLtBOK23PMeg42F7r8jchYOmglMAs=\n'
I already answered it using pycrypto here's the code
from Crypto.Cipher import DES
from base64 import b64encode
def pad(s):
return s + (DES.block_size - len(s) % DES.block_size) * \
chr(DES.block_size - len(s) % DES.block_size)
def get_params(data):
params = ''
if not data:
return params
for key, value in data.iteritems():
if key and value:
params += '{0}={1}/\\\/'.format(key, value)
return params[:-4]
def des_encrypt(plain_text):
key = '3r108w5A'
des = DES.new(key)
return b64encode(des.encrypt(pad(plain_text)))
create_params = {'cagent': 'sample',
'loginname': 'sample',
'password': 'password',
'method': 'ca',
'actype': '0',
'cur': 'USD'}
params = des_encrypt(get_params(create_params))

Why outputs in PHP and JAVA not equal

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Main {
public static void main(String[] args) {
String xmlRequest = " <request><merchant_id>46</merchant_id><order_id>33</order_id><amount>3</amount><description>hehe</description></request>";
String result = encode(xmlRequest);
String lol = "lol";
String lolResult = encode(lol);
System.out.println(result);
System.out.println(lolResult);
}
public static String encode(String toEncode) {
String hashStr = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(toEncode.getBytes());
BigInteger hash = new BigInteger(1, md.digest());
hashStr = hash.toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hashStr;
}
}
Output is:
aa4c157df0d6e95c395f2376fca94c7efa35c05d
403926033d001b5279df37cbbe5287b7c7c267fa
<?
$xml='<request><merchant_id>46</merchant_id><order_id>33</order_id><amount>3</amount><description>hehe</description></request>';
$x = sha1($xml);
echo $x;
echo sha1("lol");
?>
Output is:
d3cfa6c8ee0f7f12d28b782ac1eb45b777792e3a
403926033d001b5279df37cbbe5287b7c7c267fa
So, why string "lol" is equals and string of xml code is not?
You have an extra space - in the beginning of your java version in comparison to php, so it's not the same string

Categories