0

我已经使用适用于 Android (2.0.15) 的 Restlet 框架编写了一个 Android Web 服务客户端,并且我还编写了 Web 服务后端(同样使用 Restlet 2.0.15 JEE),它已上传到 AWS Elastic Beanstalk (因此客户端调用将采用“ http://my_web_service.elasticbeanstalk.com/this/is/my/request ”的形式)。在 HTTP 上一切正常,所以现在我想用 HTTPS 替换它,但事实证明这比我最初想象的要困难得多。

我从 Comodo 创建了一个试用 SSL 证书,我在其中将我拥有的域声明为 CNAME(不幸的是,我无法将运行 AWS 负载均衡器的 elasticbeanstalk.com 子域声明为证书主机名)。此证书已上传到我的 AWS 实例,它似乎运行成功(通过 Web 浏览器测试,在我接受浏览器上的证书后,几个 https 调用成功通过)。我唯一不喜欢这个证书的事实是,我收到一个警告,这可能是一个无效的证书,因为声明的主机名(我的域)和证书正在运行的实际主机名(elasticbeanstalk.com)不符合。

在我的客户端中,我使用的是 apache http 客户端(已在 claspath 上加载了 org.apache.httpclient.jar),这就是我创建每次调用时使用的客户端资源的方式,这很简单:

ClientResource resource = new ClientResource(resourceUri);
    Engine.getInstance().getRegisteredClients().clear();
Engine.getInstance().getRegisteredClients().add(new HttpClientHelper(null));

当然,resourceUri是“ https://my_web_service.elasticbeanstalk.com/this/is/my/request ”的形式,这是工作 HTTP 案例和非工作 HTTPS 案例的唯一区别。使用 HTTPS,我收到以下错误:

检测到可恢复错误 (1001),在 2000 毫秒内再次尝试。

我已经尝试了在谷歌上找到的几个建议(使用 org.restlet.ext.net httpclient 而不是 apache,甚至从 Restlet Android 2.1 加载 org.restlet.ext.ssl jar),但到目前为止没有任何效果。我什至使用 Wireshark 捕获了网络跟踪,这是调用流程:

|Time     | 192.168.2.111                         |
|         |                   | 54.245.249.149    |                   
|14.769   |         65430 > https [SYN]           |TCP: 65430 > https [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=16 TSval=3906214190 TSecr=0 SACK_PERM=1
|         |(65430)  ------------------>  (443)    |
|15.026   |         https > 65430 [SYN,           |TCP: https > 65430 [SYN, ACK] Seq=0 Ack=1 Win=14480 Len=0 MSS=1452 SACK_PERM=1 TSval=758478734 TSecr=3906214190 WS=256
|         |(65430)  <------------------  (443)    |
|15.027   |         65430 > https [ACK]           |TCP: 65430 > https [ACK] Seq=1 Ack=1 Win=132480 Len=0 TSval=3906214442 TSecr=758478734
|         |(65430)  ------------------>  (443)    |
|15.034   |         Client Hello                  |TLSv1: Client Hello
|         |(65430)  ------------------>  (443)    |
|15.289   |         https > 65430 [ACK]           |TCP: https > 65430 [ACK] Seq=1 Ack=124 Win=14592 Len=0 TSval=758478799 TSecr=3906214448
|         |(65430)  <------------------  (443)    |
|15.291   |         Server Hello, Certi           |TLSv1: Server Hello, Certificate, Server Hello Done
|         |(65430)  <------------------  (443)    |
|15.291   |         65430 > https [ACK]           |TCP: 65430 > https [ACK] Seq=124 Ack=1386 Win=131088 Len=0 TSval=3906214696 TSecr=758478799
|         |(65430)  ------------------>  (443)    |
|17.207   |         Client Key Exchange           |TLSv1: Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
|         |(65430)  ------------------>  (443)    |
|17.505   |         Encrypted Handshake           |TLSv1: Encrypted Handshake Message, Change Cipher Spec, Encrypted Handshake Message
|         |(65430)  <------------------  (443)    |
|17.505   |         Client Hello                  |TLSv1: Client Hello
|         |(65430)  ------------------>  (443)    |
|17.507   |         65430 > https [FIN,           |TCP: 65430 > https [FIN, ACK] Seq=557 Ack=1636 Win=130832 Len=0 TSval=3906216837 TSecr=758479353
|         |(65430)  ------------------>  (443)    |
|17.776   |         Encrypted Alert               |TLSv1: Encrypted Alert
|         |(65430)  <------------------  (443)    |
|17.776   |         65430 > https [RST]           |TCP: 65430 > https [RST] Seq=557 Win=0 Len=0
|         |(65430)  ------------------>  (443)    |
|17.777   |         Encrypted Alert               |TLSv1: Encrypted Alert
|         |(65430)  <------------------  (443)    |
|17.777   |         https > 65430 [FIN,           |TCP: https > 65430 [FIN, ACK] Seq=1682 Ack=557 Win=15616 Len=0 TSval=758479421 TSecr=3906216836
|         |(65430)  <------------------  (443)    |
|17.777   |         https > 65430 [ACK]           |TCP: https > 65430 [ACK] Seq=1683 Ack=558 Win=15616 Len=0 TSval=758479421 TSecr=3906216837
|         |(65430)  <------------------  (443)    |
|17.777   |         65430 > https [RST]           |TCP: 65430 > https [RST] Seq=557 Win=0 Len=0
|         |(65430)  ------------------>  (443)    |
|17.777   |         65430 > https [RST]           |TCP: 65430 > https [RST] Seq=557 Win=0 Len=0
|         |(65430)  ------------------>  (443)    |
|17.777   |         65430 > https [RST]           |TCP: 65430 > https [RST] Seq=558 Win=0 Len=0
|         |(65430)  ------------------>  (443)    |

从上面的调用流程来看,似乎客户端和服务器未能成功完成协商,但我不知道为什么。

欢迎任何有关如何解决此问题的建议。我认为问题存在于客户端(使用 Restlet 2.0.15 框架的 Android 应用程序),但不存在于应用程序代码本身(因为使用 HTTP 时一切正常),而是实际进行任何调用之前的 SSL 协商/握手. 我还相信证书颁发机构 (Comodo) 已被 Android 成功接受/信任(我已经通过 android 设备浏览器进行了 https 调用),但它仍然会给您一个证书警告,您需要在继续之前接受该警告。会不会是 Restlet 2.0.15 不能流畅地处理 SSL 通信,我需要升级到 2.1 或更高版本?

期待听到您的建议。如果您想获得更多可以提供帮助的信息,请问我。:)

提前致谢, 亚历克斯

========= 更新 =========

好的,正如我所怀疑的那样。问题是证书(具有 CNAME = www .mydomain.com,但从 https://mywebservice.elasticbeanstalk.com 加载)在 Android 客户端看来是无效的,因此它甚至不发送 GET/POST向服务器请求。

当我从终端发送 POST 方法(使用“curl”)时,我意识到了这一点,忽略了 ssl 验证警告(-k 选项)。这次安全连接按预期响应,发回 json 回复。

根据 Google 自己的 Android 文档建议,我尝试更改 HostnameVerifier 方法以通过认证验证。这是我的 ClientResource 当前创建的方式:

public static ClientResource createClientResource(String resourceUri) {
    Reference reference = new Reference(resourceUri);
    System.setProperty("ssl.TrustManagerFactory.algorithm",javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm());

    org.restlet.Context context = new org.restlet.Context();
    context.getAttributes().put("hostnameVerifier", new HostnameVerifier() {

                    @Override
                    public boolean verify(String arg0, SSLSession arg1) {
                            return true;

                    }

            });

    ClientResource resource = new ClientResource(context, reference);

    Engine.getInstance().getRegisteredClients().clear();
    Engine.getInstance().getRegisteredClients().add(new HttpClientHelper(null));
    Engine.getInstance().getRegisteredConverters().add(0, new JacksonConverter());

    resource.release();

    return resource;
}

但这也不起作用,我仍然收到 1001 可恢复错误。尽管如此,Android 客户端仍然无法通过“无效”请求。

我将不胜感激任何建议。:)

4

1 回答 1

0

我终于设法解决了这个问题。在检查“响应”对象,更具体地说是响应状态之后,这是错误引发的异常:

Communication Error (1001) - The connector failed to complete the communication with the server
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor  for certification path not found.

这让我重新调查了我在 AWS 上上传的证书,而这正是问题所在。看来我没有在 AWS EC2 实例上正确安装证书链(中间证书)。我的 CA 的证书链由 4 个证书文件组成,AWS 需要这个链以非常特定的顺序(首先签署证书,最后是 CA 根证书,以及介于两者之间的所有其他证书),以 pem/文本格式给出。

更正证书链后,一切都像魅力一样。:)

于 2013-05-06T17:52:56.743 回答