• Amazon S3 上传文件 SSL23_GET_SERVER_HELLO握手错误


    题外话:今天偶尔来逛逛,发现我真是懒到家了。居然有半年前的留言我都没有来看过,真对不起留言的同学,希望他的问题已经解决了。

    这两三天一直被亚马逊S3上传文件的问题困扰着,直到昨天晚上终于搞定了,工作群里一片欢腾,从客户端到服务器数位工程师卡在这个问题上抓耳挠腮了好几天,终于解决了,这就是所谓“光明总出现在最黑暗的时刻”吧,嘿嘿,非常开心,程序员真是容易满足啊。

    途中搜索了很多互联上的方案,最终在stackoverflow的一个帖子里找到有价值的参考。而我们国内的站点上关于这方面的信息非常少,所以我来写这个随笔,希望为大家增加一点参考吧。

    ------------------------------------------------------------正式开始的分割线----------------------------------------------------------------------------

     一开始我们的代码是这样,上传一个测试的mp4,我们的需求是文件size不超过10M: 

     1     File file = new File(Environment.getExternalStorageDirectory().getPath() + "/0.mp4");
     2 
     3     if(!file.exists())   
     4         return;
     5     if(!file.isFile())
     6         return;
     7     try {   
     8         URL url = new URL("https://secv.s3.amazonaws.com/familytest10099/DFG092833/2016/01/27/10-38-07/0.mp4?AWSAccessKeyId=AKIAIGMMMZARXIK3ZDBA&Expires=1453948689&Signature=uJvHirNhOlCuB5z20NPkYc73qV8%3D");//从自家server签出的S3 地址
     9     HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    10     connection.setDoOutput(true);
    11     connection.setRequestMethod("PUT");
    12     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(connection.getOutputStream());
    13     FileInputStream fileInputStream = new FileInputStream(file);// 读取文件的数据。
    14     byte[] bufer = new byte[1024];
    15     int len = 0, i = 0;
    16     while ((len = fileInputStream.read(bufer)) != -1) {
    17           bufferedOutputStream.write(bufer, 0, len);
    18     }
    19 
    20      bufferedOutputStream.flush();
    21     bufferedOutputStream.close();
    22 
    23      fileInputStream.close();
    24 
    25     int responseCode = connection.getResponseCode();
    26     Log.d("s3_ssltest","Service returned response code " + responseCode);
    27    } catch (MalformedURLException e) {
    28     // TODO Auto-generated catch block
    29     e.printStackTrace();
    30    } catch (IOException e) {
    31     // TODO Auto-generated catch block
    32     e.printStackTrace();
    33    }

        运行结果抛出异常:

    javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x54b61708: Failure in SSL library, usually a protocol error
    error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x52732cfc:0x00000000)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:448)
    at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
    at com.android.okhttp.Connection.connect(Connection.java:107)
    at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
    at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
    at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
    at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136)
    at com.spde.switchbox.manager.secu.SecuThread.a(Unknown Source)
    at com.spde.switchbox.manager.secu.SecuThread.run(Unknown Source)
    Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x54b61708: Failure in SSL library, usually a protocol error
    error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x52732cfc:0x00000000)
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:405)
    ... 11 more

    出错原因是亚马逊s3 服务器端禁用了SSLv3,解决方法是自己extends一个SSLSocketFactory把SSLv3从默认的protocol中remove掉。重写的SSLSocketFactory见附件NoSSLv3Factory.java。

    在创建connection之前先调用:

    HttpsURLConnection.setDefaultSSLSocketFactory(new NoSSLv3Factory());

    这样修改之后SSL的问题解决了,但又抛出一个异常

    javax.net.ssl.SSLException: Write error: ssl=0x5ab6f5f8: I/O error during system call, Connection reset by peer
    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method)
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:693)
    at java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:231)
    at libcore.net.http.RetryableOutputStream.writeToSocket(RetryableOutputStream.java:70)
    at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:806)
    at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
    at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
    at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
    at com.example.test.MainActivity$SSLTestRunnable.run(MainActivity.java:73)
    at java.lang.Thread.run(Thread.java:856)


    原因是http header中没有指定content-type,传输格式是stream

    connection.setRequestProperty("Content-Type", "application/octet-stream");

    这个异常也解决了。访问成功,但是返回403 Forbidden,网上搜到很多解释说是客户端的时区或者系统时间不对,因为S3上签出的url是有expire date的。但我们出问题的原因是因为服务器签出这个url的时候没有指定Content-type , 又是Content-type的问题,修改签出地址的代码如下:

    public static String createUploadUrl(String bucket, String objKey, Date expire){
        String url = s3Client.generatePresignedUrl(new GeneratePresignedUrlRequest(bucket, objKey).
                    withMethod(HttpMethod.PUT).
                    withContentType("application/octet-stream").
                   withExpiration(expire)
                ).toString();
        return url;
    }

    ok,这次终于收到回复200 ok了,视频也上传成功。

    困扰三天的问题就这样解决了。总结一下:一个复杂迷茫各种想不通的问题,最终解决的时候往往修改不了几行代码。

    额外的说明:我们出问题的平台android版本是4.4.3,openSSL lib的版本是1.0.1e。网上搜集到的信息,SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure 这种问题通常出现在Android 4.x上。

    最后是 NoSSLv3Factory.java 的源码

     
  • 相关阅读:
    [LeetCode] Rabbits in Forest 森林里的兔子
    [LeetCode] 780. Reaching Points 到达指定点
    [LeetCode] Swim in Rising Water 在上升的水中游泳
    [LeetCode] 777. Swap Adjacent in LR String 交换LR字符串中的相邻项
    [LeetCode] Split BST 分割二叉搜索树
    [LeetCode] Global and Local Inversions 全局与局部的倒置
    [LeetCode] Minimize Max Distance to Gas Station 最小化去加油站的最大距离
    [LeetCode] Sliding Puzzle 滑动拼图
    [LeetCode] Basic Calculator IV 基本计算器之四
    [LeetCode] Jewels and Stones 珠宝和石头
  • 原文地址:https://www.cnblogs.com/inkheart0124/p/5165948.html
Copyright © 2020-2023  润新知