我用 Java 编写的 Java 客户端库出现 SSL 错误。遵循文档后请求仍然失败



我正在创建一个电子商务网站,我计划使用的一个托运人提供了一个可用于自动下单和跟踪订单的api。尽管他们有几种语言的客户端(PHP、Python、Ruby、Node),但他们还没有Java版本的客户端,所以我决定自己编写。我正在尝试测试它,但我一直收到这个错误

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

我遵循了他们的文档说明:

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

我已经按照的指示

//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;
}

我不明白为什么我会犯这个错误。我试着给该公司的开发团队发电子邮件,他们建议我尝试创建一个虚假的商店,它与任何我可以用来测试的电子商务软件都没有连接,但他们说一切看起来都很好。我试着遵循那个建议,但还是犯了同样的错误。

我在Stackoverflow上发现了这个线程,看起来有人遇到了类似的问题。我只是想知道,当其他语言的其他客户端库都不需要经过这个过程时,为什么我必须在Java中这样做?

据我所知,我正在按照文档中所说的方式对所有内容进行编码,并且我正在正确设置标题。我需要使用不同的库吗?

我目前正在使用apachecommons编解码器

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

和apachehttp组件

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

我试着用另一种语言运行该公司的一个客户端库中的示例,一切都正常。我知道这是我的代码出了问题,而不是他们的SSL证书,但我不知道是什么。

您的服务器正在使用服务器名称指示来根据您要求的名称提供不同的证书(这对于在同一IP地址和端口上托管多个具有SSL/TLS的证书是必要的)。

您可以使用进行检查

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

openssl s_client -connect api.theprintful.com:443

SNI目前仅在Java 7之后才可用(并且仅在客户端,计划为Java 8提供服务器端支持)。

当使用HttpsURLConnection时,一个执行Java7的应用程序应该自动为您执行此操作。

Apache HTTP客户端中对此的修复程序是最新的。您应该确保您确实在使用4.3.2版本Java 7。以下操作将在4.3.1版(即使使用Java 7)或4.3.2版Java 6上失败:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("https://sni.velox.ch/");
HttpResponse response = httpClient.execute(httpGet);

您是否通过SSL连接?也就是说,你的路是从‘https://www.behappy.me"?如果是这样的话,那么我认为发生的事情是他们的SSL证书是为"api.theprintful.com"颁发的。为了防止中间人攻击,SSL客户端应该检查他们从服务器获得的证书是否真的是为有问题的服务器颁发的,请参阅RFC 6125。在您的情况下,此检查失败并导致您看到的异常。这与你方的身份验证无关,你可能做得很好,他们看到了正确的请求,但你方在错误的证书上大喊大叫。

你能做些什么来解决这个问题?

  1. 要求他们提供正确的证书。这是最简单、最好的解决方案。不幸的是,这需要他们一方的合作,而你可能会得到,也可能不会得到
  2. 伪造你自己的DNS,也就是说,在你的主机文件中放一个指向api.theprintful.com的IP地址www.behappy.me的条目。这是本线程中的建议
  3. 通过提供一个空的HostnameVeriier来抑制主机名检查。这在上面的同一个线程中指出,并且可以在不干扰任何外部设置的情况下完成。但请确保,我的意思是,在将代码转移到生产环境之前,确实绝对确保删除了该解决方法,否则就会失败

最新更新