我使用Android的Restlet框架(2.0.15)编写了一个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证书,我已经在其中声明了我拥有的域名(不幸的是,我无法声明运行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毫秒。
我尝试了一些我在google上找到的建议(使用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.mlasticbeanstalk.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客户端无法通过“无效”请求。
我非常感谢任何建议。 :)
答案 0 :(得分: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 / text格式给出。
在纠正证书链后,一切都像魅力一样。 :)