2

​I am facing problem while connecting with WooCommerce API (http://woothemes.github.io/woocommerce-rest-api-docs/#authentication). I am successfully able to generate the signature using java program and also checked the generated signature having same time stamp and nonce on linked in console(as given in the WooCommerce document) for any discrepancy but the generated signature is same on linked in and java output console.

The process of our working is described below :

​1. We have generated the signature with the help of signature base string and secret key using java program. Signature base string looks like :

GET&http%3A%2F%2FEndPointURL%2Fwc-api%2Fv2%2Forders&oauth_consumer_key%3D%26oauth_nonce%3D70810941%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1433226349%26oauth_version%3D1.0

2. ​While we are trying to access the url http://EndPointURL/wc-api/v2/orders, It is showing error given below :

{"errors":[{"code":"woocommerce_api_authentication_error","message":"Invalid Signature - provided signature does not match"}]}

3.We have also generated the signature with the help of Linked in test Console using same value of time stamp and nonce and getting the same signature. But we are not able to access the data.

The java code that I am using is given below :

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;


public class OAuthForWooCommerce {

private static String key = "consumer_Key";
private static String secret = "consumer_Secret";

private static final String HMAC_SHA1 = "HmacSHA1";

private static final String ENC = "UTF-8";

private static Base64 base64 = new Base64();


private static String getSignature(String url, String params)
        throws UnsupportedEncodingException, NoSuchAlgorithmException,
        InvalidKeyException {
    /**
     * base has three parts, they are connected by "&": 1) protocol 2) URL
     * (need to be URLEncoded) 3) Parameter List (need to be URLEncoded).
     */
    StringBuilder base = new StringBuilder();
    base.append("GET&");
    base.append(url);
    base.append("&");
    base.append(params);
    System.out.println("String for oauth_signature generation:" + base);
    // yea, don't ask me why, it is needed to append a "&" to the end of
    // secret key.
    byte[] keyBytes = (secret + "&").getBytes(ENC);

    SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);

    Mac mac = Mac.getInstance(HMAC_SHA1);
    mac.init(key);

    // encode it, base64 it, change it to string and return.
    return new String(base64.encode(mac.doFinal(base.toString().getBytes(
            ENC))), ENC).trim();
}


public static void main(String[] args) throws ClientProtocolException,
        IOException, URISyntaxException, InvalidKeyException,
        NoSuchAlgorithmException {
     System.out.println("*** Welcome to WooCommerce Klipfolio integration Wizard ***");
    HttpClient httpclient = new DefaultHttpClient();
    List<NameValuePair> qparams = new ArrayList<NameValuePair>();
    // These params should ordered in key
    //qparams.add(new BasicNameValuePair("oauth_callback", "oob"));
    qparams.add(new BasicNameValuePair("oauth_consumer_key", key));
    String nonce = RandomStringUtils.randomAlphanumeric(32);
    //String nonce2 = URLEncoder.encode(nonce1, "UTF-8");
    qparams.add(new BasicNameValuePair("oauth_nonce", nonce));
    //qparams.add(new BasicNameValuePair("oauth_nonce", ""+ (int) (Math.random() * 100000000)));
    qparams.add(new BasicNameValuePair("oauth_signature_method",
            "HMAC-SHA1"));
    qparams.add(new BasicNameValuePair("oauth_timestamp", ""
            + (System.currentTimeMillis() / 1000)));
    qparams.add(new BasicNameValuePair("oauth_version", "1.0"));

    // generate the oauth_signature
    String signature = getSignature(URLEncoder.encode(
            "http://MY_END_URL/wc-api/v2/orders", ENC),
            URLEncoder.encode(URLEncodedUtils.format(qparams, ENC), ENC));
    System.out.println("Getting Oauth Signature...");
    // add it to params list
    qparams.add(new BasicNameValuePair("oauth_signature", signature));

    // generate URI which lead to access_token and token_secret.
    URI uri = URIUtils.createURI("http", "MY_END _URL", -1,
            "wc-api/v2/orders",
            URLEncodedUtils.format(qparams, ENC), null);

    System.out.println("Connecting to  the URL : \n"
            + uri.toString());

    HttpGet httpget = new HttpGet(uri);
    // output the response content.
    System.out.println("Getting Response from the server :");

    HttpResponse response = httpclient.execute(httpget);
    HttpEntity entity = response.getEntity();
    if (entity != null) {
        InputStream instream = entity.getContent();
        int len;
        byte[] tmp = new byte[2048];
        while ((len = instream.read(tmp)) != -1) {
            System.out.println(new String(tmp, 0, len, ENC));
        }
    }
}

}

Please let me know where I am doing wrong.

Thanks,

Archit
  • 57
  • 2
  • 8

2 Answers2

0

You must remove the version number from params, according to the documentation. I think also you should remove "&" you added to the secret key when generating signature, since I managed to get 200 response without it.

The require parameters are: oauth_consumer_key, oauth_timestamp, oauth_nonce, oauth_signature, and oauth_signature_method. oauth_version is not required and must be omitted.

You should also sort the parameters in byte order before adding them to the string you used to create the signature.

Also, I suggest you to debug by changing the message line to see the signature you sent to the server and the signature you should have sent.

For instance, you can change it to:

throw new Exception( __( 'Invalid Signature - provided signature does not match Secret:' . $user->woocommerce_api_consumer_secret . ', StringToSign: ' . $string_to_sign . ', TakenSign: ' . $consumer_signature . ', GeneratedSign: ' . $signature, 'woocommerce' ), 401 );
0

Thanks for your code Archit, this helped me jump through hoops.

I have updated your code and the below works.

package com.woocommerce.experiments;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class OAuthForWooCommerce {

  private static String key = "ck_your_consumer_key";
  private static String secret = "cs_your_consumer_secret";

  private static final String HMAC_SHA1 = "HmacSHA1";

  private static final String ENC = "UTF-8";

  private static Base64 base64 = new Base64();

  private static String getSignature(String url, String params)
          throws UnsupportedEncodingException, NoSuchAlgorithmException,
          InvalidKeyException, EncoderException {
    /**
     * base has three parts, they are connected by "&": 1) protocol 2) URL
     * (need to be URLEncoded) 3) Parameter List (need to be URLEncoded).
     */
    StringBuilder base = new StringBuilder();
    base.append("GET&");

   //follow Step 2 and encode
    base.append(new URLCodec(Consts.UTF_8.displayName()).encode(url));
    base.append("&");
    base.append(params);

    System.out.println("String for oauth_signature generation: " + base);

    byte[] keyBytes = (String.format("%s", secret)).getBytes(ENC);

    SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);

    Mac mac = Mac.getInstance(HMAC_SHA1);
    mac.init(key);

    // encode it, base64 it, change it to string and return.
    String signature = new String(base64.encode(mac.doFinal(base.toString().getBytes(ENC))), ENC).trim();
    return signature;
  }


  public static void main(String[] args) throws IOException, URISyntaxException, InvalidKeyException,
          NoSuchAlgorithmException, EncoderException {

    String nonce = RandomStringUtils.randomAlphanumeric(32);

    Map<String, String> paramMap = new TreeMap<>();
    paramMap.put("oauth_consumer_key", key);
    paramMap.put("oauth_timestamp", "" + (System.currentTimeMillis() / 1000));
    paramMap.put("oauth_nonce", nonce);
    paramMap.put("oauth_signature_method", "HMAC-SHA1");

    List<NameValuePair> qparams = new ArrayList<>();

    //uksort( $params, 'strcmp' ) mimic
    paramMap.entrySet().stream().forEach(stringStringEntry -> qparams.add(new BasicNameValuePair(stringStringEntry.getKey(), stringStringEntry.getValue())));

    //double encode Step 3 (in order to replace '%' with '%25') after sorting (Step 4)
    String encodedParams = URLEncoder.encode(URLEncodedUtils.format(qparams, ENC), ENC);

    System.out.println("Encoded Params "+ encodedParams);

    String signature = getSignature("http://your_end_url/wc-api/v2/orders", encodedParams);

    qparams.add(new BasicNameValuePair("oauth_signature", signature));

    HttpClient httpclient = HttpClientBuilder.create().build();

    System.out.println("Getting Oauth Signature...");

    // generate URI which lead to access_token and token_secret.
    URI uri = URIUtils.createURI("http", "your_end_url", -1, "wc-api/v2/orders", URLEncodedUtils.format(qparams, ENC), null);

    System.out.println("Connecting to  the URL : \n" + uri.toString());

    HttpGet httpget = new HttpGet(uri);
    httpget.setHeader("X-Stream" , "true");

    // output the response content.
    System.out.println("Getting Response from the server :");

    HttpResponse response = httpclient.execute(httpget);
    HttpEntity entity = response.getEntity();

    System.out.println("Response Code " + response.getStatusLine().getStatusCode());

    if (entity != null) {
      System.out.println("Json response" + IOUtils.toString(entity.getContent(), ENC));
    }
  }
}

I hope this helps you (hopefully not late!) and others. Step by step process for authentication can be found at WooCommerce REST API Documentation

Community
  • 1
  • 1
OliverTester
  • 401
  • 2
  • 7