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

我试图将file upload到受SSL保护的Web服务,并需要客户端证书(由内部CA签名)。 除了上传文件之外,与networking服务的通信运行良好(下载文件,查询,运行命令以及执行各种POST的工作正常)。

当上传文件时,我得到一个SSLException(javax.net.ssl.SSLException),其中写道:“写入错误:ssl = 0x5fe209c0:系统调用期间的I / O错误,由对等方重置连接”。

我已经创build了一个重复的服务器,并删除了SSL和客户端证书的要求,并尝试上传'香草'的HTTP,它的工作完美。

我试过使用setFixedLengthStreamingMode(int)和setChunkedStreamingMode(int)没有成功。 当使用它们时,从write方法抛出exception,并且当不使用它们中的任何一个时,从调用getResponseCode()抛出同样的exception。

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

我们的另一个客户端(iOS客户端)能够在那里上传文件,所以它一定是我做的 – 但我不知道是什么。

我不知道如何进一步debugging这个问题。

请帮忙。

编辑1

我们做了很多debugging工作,发现:

  • 如预期的那样上传小文件(44kb是成功上传的最大文件的大小,并且以〜1200ms上传)。
  • 一个46kb文件无法上传。 失败花了约2分钟(134120ms)。

编辑2

之后你会看到的话,现在我得到了小提琴演奏很好(感谢这个问题 )。 提琴手得到的文件,但没有成功发送。 请求(原始)如下所示:

 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 

提琴手的回应(也是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”和详细的“Tracing”。 MessageLogging不显示消息的任何提示(可能在转成消息之前丢弃),但跟踪显示: 从SvcTraceViewer看到的WCF跟踪

现在,在你说“啊,这是一个服务器问题”之前,请记住,44kb文件成功上传,我们的iOS应用程序也能成功上传文件。

这是来自客户端得到的exception的调用堆栈:

 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) 

不是一个答案,更多的工作 – 供您参考。

在这个问题上抨击我们,并做了大量的研究后,我们放弃了。 我们已经与谷歌开放这个问题 ,并实施了以下工作:

为了上传文件,应用程序首先通过需要客户端证书的端点获取上载令牌 ,之后使用该令牌上传到不需要客户端证书的端点(但仍然通过SSL(Https) )。

是的,这只是一个小小的安全漏洞,但我们必须这样做。 我们尽可能保护它…

我保证更新Google的票证将被更新(并希望解决)。

HTH!

这是晚了一点(因为你已经实施了一个解决方法),但是这应该解决这个问题: https : //stackoverflow.com/a/9224892/1619545

我们遇到同样的问题,设置客户端证书协商标志启用似乎是唯一的帽子帮助。 在这里看看如何在证书绑定中更改标志:

http://help.sap.com/saphelp_smp305svr/helpdata/en/6f/f0a9b6e1c743d48d1e57235d297c1c/content.htm