使用客户端证书和Android的HttpsURLConnection上传SSL文件

25
我试图将文件上传到一个受SSL保护并需要客户端证书(由内部CA签名)的Web服务。与Web服务的通信正常工作(下载文件、查询、运行命令和执行各种POST操作都按预期正常工作),但上传文件除外
当上传文件时,我会收到一个SSLException(javax.net.ssl.SSLException),其中写道“写入错误:ssl=0x5fe209c0:系统调用期间的I/O错误,连接被对等方重置”。
我创建了一个副本服务器并删除了SSL和客户端证书要求,并尝试通过“vanilla”HTTP上传,它完美地工作。
我已经尝试使用setFixedLengthStreamingMode(int)setChunkedStreamingMode(int),但没有成功。当使用它们时,异常从write方法抛出,而不使用它们时,相同的异常从调用getResponseCode()处抛出。

我在服务器的 EventVwr 中没有找到关于错误的任何信息。

我们的另一个客户端(iOS客户端)能够上传文件,所以问题肯定出在我的地方,但我无法弄清楚是什么问题。

我不确定如何进一步调试此问题。

请帮忙。

编辑1

我们已经做了很多调试工作,发现:

  • 小文件可以正常上传(44kb是成功上传的最大文件大小,上传时间约为1200毫秒)。
  • 一个46kb的文件上传失败。失败耗时约2分钟(134120毫秒)。

编辑2

根据备注中所述,现在我让Fiddler正常工作了(感谢this question)。Fiddler已经获得了该文件,但未能成功发送。

请求(原始)看起来像:

POST https://192.168.2.2/rest/transfer/strong/Upload/Full?Path=%5C20140807_113255_20.jpg&Root=2 HTTP/1.1
SessionToken: 1234 // We use this for session management
FileMetadata: {"FileSize":"1315496","FileName":"GrumpyCat.jpg"}
Connection: Keep-Alive
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.1; GT-N7100 Build/JRO03C)
Host: 192.168.2.2
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded
Content-Length: 1315496

;odiao;awriorijgoeijoeirj;oedfrvgerg... // The image

Fiddler的响应(也是RAW)如下:

HTTP/1.1 504 Fiddler - Send Failure
Date: Wed, 20 Aug 2014 17:40:29 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Timestamp: 20:40:29.420

[Fiddler] ResendRequest() failed: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host. < An existing connection was forcibly closed by the remote host                                                                                                                                                                                                                                                                                                              

此外,我们已经添加了WCF的“MessageLogging”和详细的“跟踪”。MessageLogging没有显示任何消息的提示(可能在转换为消息之前被丢弃),但跟踪显示了以下内容: The WCF Trace as seen from the SvcTraceViewer 现在,在您说“啊哈,这是服务器问题”之前,请记住44kb文件上传成功,并且我们的iOS应用程序也能够成功上传文件。
这是客户端收到的异常的调用堆栈:
E/RestClientUploader(3196): javax.net.ssl.SSLException: Write error: ssl=0x5d94b8b0: I/O error during system call, Connection reset by peer
E/RestClientUploader(3196):     at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method)
E/RestClientUploader(3196):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:693)
E/RestClientUploader(3196):     at java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:231)
E/RestClientUploader(3196):     at libcore.net.http.ChunkedOutputStream.writeBufferedChunkToSocket(ChunkedOutputStream.java:129)
E/RestClientUploader(3196):     at libcore.net.http.ChunkedOutputStream.write(ChunkedOutputStream.java:77)
E/RestClientUploader(3196):     at java.io.DataOutputStream.write(DataOutputStream.java:98)
E/RestClientUploader(3196):     at com.varonis.datanywhere.communication.RestClientUploader.uploadFileToServer(RestClientUploader.java:151)
E/RestClientUploader(3196):     at com.varonis.datanywhere.communication.RestClientUploader.uploadFullFile(RestClientUploader.java:67)
E/RestClientUploader(3196):     at com.varonis.datanywhere.communication.services.FileUploadService.doUpload(FileUploadService.java:128)
E/RestClientUploader(3196):     at com.varonis.datanywhere.communication.services.FileUploadService.onHandleIntent(FileUploadService.java:98)
E/RestClientUploader(3196):     at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
E/RestClientUploader(3196):     at android.os.Handler.dispatchMessage(Handler.java:99)
E/RestClientUploader(3196):     at android.os.Looper.loop(Looper.java:137)
E/RestClientUploader(3196):     at android.os.HandlerThread.run(HandlerThread.java:60)

1
Fiddler在向服务器发送的流量方面报告了什么?当它代理您的请求时会发生什么,服务器会给出什么响应? - Deepak Bala
@DeepakBala - 这是我检查的第一件事情之一(现在重新检查)- 但从iOS客户端上传文件(大于1mb)非常顺利。这不是服务器配置问题。 - Felix
很抱歉,我对WCF的了解不足,无法提供有关问题的进一步线索。不过看起来你已经在正确的轨道上了。找到解决方案后,请告诉我们。 - Deepak Bala
1
这可能与此问题有关:https://code.google.com/p/android/issues/detail?id=61706 - Luis
@Leco,我不认为是这样的。 虽然我不能百分之百确定,但我可以在WireShark中看到服务器在2分钟后发送了FIN - 这告诉我连接仍然存在 - 并且这个FIN导致Android设备的连接被“对等方重置”。 - Felix
显示剩余13条评论
2个回答

2
不是答案,更多的是一个解决方法,供您参考。 在处理这个问题时,我们做了很多研究却仍无法解决。我们已经向Google提出此问题,并实施了以下解决方法:
为了上传文件,应用程序首先通过需要客户端证书的端点获取一个上传令牌,然后使用此令牌上传到不需要客户端证书但仍通过SSL(Https)传输的端点。
是的,这是一种次要的安全漏洞,但我们不得不这样做。我们已尽可能地保护它。
我承诺在Google的工单更新(并希望解决)时进行更新。

提供的链接似乎需要一个账户。您是否有更新此链接的任何新进展的能力? - halfer

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接