4

I am working on implementing the Walmart API in NodeJS. Walmart only provides JAVA examples. I am having issues getting it right. My signature is a bit longer and not accepted when compared to using the Java executable they provide.

I'd appreciate any help. Even figuring out what format the Walmart provided secret is would help.

Here's working Java code:

import org.apache.commons.codec.binary.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;

public class SHA256WithRSAAlgo {
    private static String consumerId = "b68d2a72....";   // Trimmed for security reason
    private static String baseUrl = "https://marketplace.walmartapis.com/v2/feeds";
    private static String privateEncodedStr = "MIICeAIBADANBgkqhkiG9w0BAQEFAA......";       //Trimmed for security reasons
    public static void main(String[] args) {
        String httpMethod = "GET";
        String timestamp = String.valueOf(System.currentTimeMillis());
        String stringToSign = consumerId + "\n" +
                                baseUrl + "\n" +
                                httpMethod + "\n" +
                                timestamp + "\n";
        String signedString = SHA256WithRSAAlgo.signData(stringToSign, privateEncodedStr);
        System.out.println("Signed String: " + signedString);
    }
    public static String signData(String stringToBeSigned, String encodedPrivateKey) {
        String signatureString = null;
        try {
            byte[] encodedKeyBytes = Base64.decodeBase64(encodedPrivateKey);
            PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(myPrivateKey);
            byte[] data = stringToBeSigned.getBytes("UTF-8");
            signature.update(data);
            byte[] signedBytes = signature.sign();
            signatureString = Base64.encodeBase64String(signedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signatureString;
    }
}

Here's my Node code:

const PK_HEADER = '\n-----BEGIN PRIVATE KEY-----\n'
const PK_FOOTER = '\n-----END PRIVATE KEY-----\n'

const consumerId = 'b68d2a72....'  
const baseUrl = 'https://marketplace.walmartapis.com/v2/feeds'
const privateEncodedStr = 'MIICeAIBADANBgkqhkiG9w0BAQEFAA......'      

const privateKey = `${PK_HEADER}${config.walmart.secret}${PK_FOOTER}`

const privateEncodedKey = Buffer.from(privateKey, 'base64')
const timestamp = Date.now()

const stringToSign = consumerId + '\n' +
                     baseUrl + '\n' +
                     httpMethod + '\n' +
                     timestamp + '\n'

const sign = createSign('RSA-SHA256')
sign.update(stringToSign)
const signedString = sign.sign(privateKey, 'hex')

console.log(signedString)

Some notes. I tried passing the privateEncodedStr as is but Crypto balks at not having the PEM headers so I had to add those.

Using the same timestamp the Java code yielded something like:

bhG0q4Es7iOJtBvepJ2Ao6zPRllf6nM+026dgEadPcaYDdIoCQBYxWWSXB16XcQXgCDcqZ1PW2xgAavHC57jchSXtsTYkuXcWBavQGTH+5YonxIJCzI0wimVKKbqtocKvz4sngXKvIDP7wKKUdXOT6zXVYOdjLfUTERTs7RVg=

My JS code:

219af9f3048ccef558d6ddeeb61d19ed8a968ade5125760d81717dbd62e8447dd831b123a52624d56bc35aef1b082c29585e6fece2aba0fb7853d6840f45e724489028415a9eab8a51e48037a5884f5a12a238ed61a16003e1c412f873d3cfd2f6336dec8c262b01c3ba2a234f0979b8073f096cd35c7d1425bbcfc4603ff05b

Here's what the Walmart secret looks kinda like:

MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMCCJeju5NFCHc/53N0AjZnmWq0UYZyrh3jRZH3UAISeZmLxcAgO65Cg8SfRact172Iy1uYnCB4NqKT5+x0BelWHA2fci5/7VqAdTJ3iZWB1g61lLUpf0IiesMWAycMBf7GwaVIsCGoPb+IC6l1P5IV2Mtb29WnivQyE3UM4dLvCvejdyPq8IoBIywTAgMBAAECgYEAnZ7yo1JXD+9usYcIC/wT9Nrji6uQcNMRTR9FhLE861k2w/Sjok0kzepZjanNojgwQS6OWIy3VEkRj2bTO7N4s6ApLa8yxoQt6ZrOSId9Ut7IenZQ39c6c/ig6e+awyjGvJlKdf7wtv2i4l4tiL8w23RnSECQQDy0wugM5hzV5wc/ejj+9cB8cqdEjfFG8yBZ200W1DAQwepIWFqSdiHbHW9xX5SiFa7JsDHyFWSdboYQToRDHuVAkEAyvQwDqAAz/FJn9oFKXznMjFfrtfoZ/BPrc/4BOUiyWjitxE6Ia5rNuTgCq3u1XxxP78t5spzMr5H5QdoUFHfBwJAbqpYZg6dsBOBhoUBmsWv26cyyOCEg4h9113oDh3MBPEXcQJAHd2JJN3OwMrU9rzyfYRv0ScK2YPUI1dtojo0WSQr2UafYTCCnEstN5vPvDoSvwXO1myk0COs1kAAbveHMIsf1jKVx7euXYP1J8Zwdfd9FjS2CQJAZ8L+jQyGlYIIdUQA7n0bbUblsGntsk1RKkTuZ31Q4w5gBnKO4dk3WMVhNdhmJnZRHRNflA41TITLNYh0EnrWheadVrhpBm2YBn7WFPQ==

Any idea what format this is? It appears to be a private key with the headers/footers removed.

5
  • this is a bit unrelated but if you do figure it out would you be able to post on github? cheers! Commented Nov 13, 2016 at 18:49
  • Your resulting JS string seems to be an hex byte sequence, while the Java generated one is encoded in base64. Try console.log(new Buffer(signedString, 'hex').toString('base64')) Commented Nov 13, 2016 at 23:10
  • @karuzo you're my hero! that was it. Would you post that as the answer please. Commented Nov 13, 2016 at 23:17
  • Turns out that just changing the 'hex' to 'base64' was all I needed to do. Thanks for catching that Commented Nov 13, 2016 at 23:19
  • Ok, I've posted an updated answer. Commented Nov 13, 2016 at 23:25

1 Answer 1

3

Your code is right, you just need to output signedString in the proper format, which is base64.

Just change:

const signedString = sign.sign(privateKey, 'hex');

To:

const signedString = sign.sign(privateKey, 'base64');

And there you go.

Sign up to request clarification or add additional context in comments.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.