0

I'm starting an e-commerce site and one of the drop shippers that I plan on using offers an api that can be used to automate the placing and tracking of orders. Even thought they have clients in several languages (PHP, Python, Ruby,Node), they don't have one in Java yet so I decided to write my own. I'm trying to test it, but I keep getting this error

Hostname in certificate didn't match: <api.theprintful.com> != <behappy.me> OR <behappy.me> OR <www.behappy.me>

I've followed the directions that they have for their documentation:

Some API requests (like the Product catalog and Country codes) are available without an  
API key, but for majority of requests you will need to authenticate your store.
...
To perform authorization, you need to add the Authorization header with a Base64 encoded
API key when performing a request. So if the API key is vv0gtldg-1d7v-qclq:e2vv-
lmhg676ak0z1, then you need to add this header to the API request:
Authorization: Basic dnYwZ3RsZGctMWQ3di1xY2xxOmUydnYtbG1oZzY3NmFrMHox

I've followed the directions

//constructor that is used to make the object
public ProductsRequest(String apiKey)
{
    super();
    this.apiKey = apiKey;
    this.encodedApiKey = codec.encodeBase64String(apiKey.getBytes());
}

//function that is used to call the api
public List<Product> getAllProductList(String path)
{
    //the list of products that will be returned
    List<Product> returnedProducts = null;

    //set the endpoint that will be used to get the list of products
    httpGet = new HttpGet(path);
    String basicString = "Basic "+this.encodedApiKey;
    httpGet.addHeader("Authorization",basicString.substring(0,basicString.length()-2));

    try
    {
        response = httpClient.execute(httpGet);
        if(response.getStatusLine().getStatusCode() == 200)
        {
            entity = response.getEntity();
            jsonResponse = EntityUtils.toString(entity);
            Type listType = new TypeToken<List<Product>>(){}.getType();
            JsonNode root = mapper.readTree(jsonResponse);
            ArrayNode products = (ArrayNode) root.path("result");
            returnedProducts = (List<Product>) gson.fromJson(products.toString(),listType);
        }
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return returnedProducts;
}

I can't figure out why I'm getting this error. I tried emailing the dev team at the company and they suggested that I try creating a fake store that isn't connected to any ecommerce software that I can use just for testing but they said that everything looked good on their end. I tried following that suggestion but I still get the same error.

I found this thread on Stackoverflow where it looks like somebody had a similar problem. I'm just wondering why would I have to do this in Java when none of the other client libraries in other languages have to go through this process?

As far as I can tell I'm encoding everything the way the documentation says I should and I'm setting the headers correctly. Do I need to use different libraries?

I'm currently using the apache commons-codec

<dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.4</version>
  </dependency>

and apache http-components

<dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.3.2</version>
  </dependency>

I've tried running the examples from one of the client libraries that the company has in another language and everything is working correctly. I know that it is something wrong with my code and not their SSL certificates, but I can't figure out what it is.

Community
  • 1
  • 1
j.jerrod.taylor
  • 1,120
  • 1
  • 13
  • 33

2 Answers2

0

Are you connecting via SSL? i.e. does your path begin with 'https://www.behappy.me'? If so, then i think what's happening is that their SSL certificate is issued for 'api.theprintful.com'. To prevent man-in-the-middle attacks, SSL clients are supposed to check whether the certificate they got from the server was really issued for the server in question, see RFC 6125. In your case, this check fails and results in the exception you see. This has nothing to do with your side of the authentication, you are probably doing everything right, they see the correct request coming in, but your side croaks on the wrong certificate.

What can you do to get around this?

  1. Ask them to provide the correct certificate. That's the easiest and best solution. Unfortunately, it requires cooperation from their side, which you may or may not get.
  2. Fake you own DNS, that is, put an entry in your hosts file pointing api.theprintful.com to the IP address of www.behappy.me. This is the suggestion in this thread.
  3. Suppress the hostname check by supplying an empty HostnameVeriier. That's indicated in the same thread above, and can be done without messing with any external settings. But make sure, i mean really absolutely sure that you remove that workaround before moving the code to production, or else goto fail.
Community
  • 1
  • 1
wallenborn
  • 4,158
  • 23
  • 39
  • The company that I'm using runs two different websites. One is theprintful.com and the other one is behappy.me. The printful is the one that offers the api. What do I need to do about a certificate. I don't have one and there is nothing about using one in their documentation. – j.jerrod.taylor Mar 05 '14 at 17:00
  • Providing the correct certificate is their job. Anyone who sets up a SSL server on say www.example.com is obliged to make sure to provide a certificate issued for 'https://www.example.com'. Maybe they wanted to save money by getting only one cert, maybe it's a simple mixup on their side. – wallenborn Mar 05 '14 at 17:04
  • Well, I tried emailing the dev team again. They were pretty responsive the first time so I'm guessing they will be this time as well. Would it still make sense for me to try what you suggested above? – j.jerrod.taylor Mar 05 '14 at 17:16
  • I would rather have them fix their server than try to work around it on my side. But of course, if they don't, you'll have no other choice. – wallenborn Mar 05 '14 at 17:21
  • I downloaded and installed node to test whether their node client would work. It worked without any problems. It is definitely something that I'm doing in my Java code. – j.jerrod.taylor Mar 05 '14 at 21:14
0

Your server is using Server Name Indication to present a different certificate depending on the name you ask for (which is necessary to host multiple certificate with SSL/TLS on the same IP address and port).

You can check this with:

openssl s_client -connect api.theprintful.com:443 -servername api.theprintful.com

and

openssl s_client -connect api.theprintful.com:443

SNI is currently only available since Java 7 (and only on the client side, server-side support being planned for Java 8).

An application doing Java 7 should do this automatically for you when using HttpsURLConnection.

The fix for this in Apache HTTP Client is recent. You should make sure you are indeed using version 4.3.2 and Java 7. The following would fail with version 4.3.1 (even with Java 7), or with 4.3.2 on Java 6:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("https://sni.velox.ch/");
HttpResponse response = httpClient.execute(httpGet);
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • Just in case check the 3 lines (against sni.velox.ch) and also check you're indeed using the right JDK by printing `System.getProperty("java.version")`. It's hard to say from your latest commit that you've switched JDK. You should also put the Java version in your maven compiler options in your `pom.xml`. – Bruno Mar 12 '14 at 00:59
  • Did you try to print `System.getProperty("java.version")`? – Bruno Mar 12 '14 at 15:18
  • You latest additional test (against `sni.velox.ch`) works for me on Java 7u51. It fails because you don't get the expected type format, but the connection works. Whatever IDE you're using, make sure it's running your code with the JRE you expect. The options you'd need in the POM would be `source` and `target` not `compilerVersion` (only with `fork`). – Bruno Mar 12 '14 at 15:40