SSL不使用http-kit但使用普通Java

时间:2017-01-10 22:46:18

标签: java ssl clojure iis-7

我在使用http-kit向运行IIS7的Web服务器发送请求时遇到问题。当我打开调试模式时,我看到的就是在发送get请求之后,显然,我得到了几个小包和一堆“Ignoring unsupported cipher suite:”,然后是超时:

%% Cached client session: [Session-22, TLS_RSA_WITH_AES_128_CBC_SHA]
[read] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C A4 24 50 40   A5 F2 3B DD 01 BB 7C A2  .....$P@..;.....
Padded plaintext before ENCRYPTION:  len = 208
0000: 47 45 54 20 2F ## ## ##   ## ## ## ## ## ## ## ##  GET /###########
0010: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0020: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0030: ## ## ## ## ## ## ## 20   48 54 54 50 2F 31 2E 31  ####### HTTP/1.1
0040: 0D 0A 48 6F 73 74 3A 20   ## ## ## ## ## ## ## ##  ..Host: ########
0050: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0060: ## ## ## ## ## 0D 0A 55   73 65 72 2D 41 67 65 6E  #####..User-Agen
0070: 74 3A 20 68 74 74 70 2D   6B 69 74 2F 32 2E 30 0D  t: http-kit/2.0.
0080: 0A 41 63 63 65 70 74 2D   45 6E 63 6F 64 69 6E 67  .Accept-Encoding
0090: 3A 20 67 7A 69 70 2C 20   64 65 66 6C 61 74 65 0D  : gzip, deflate.
00A0: 0A 43 6F 6E 74 65 6E 74   2D 4C 65 6E 67 74 68 3A  .Content-Length:
00B0: 20 30 0D 0A 0D 0A AF DD   9B 8A C3 9A BB BE 20 8B   0............ .
00C0: 94 AB 63 0F 74 80 4D 59   F4 73 05 05 05 05 05 05  ..c.t.MY.s......
client-loop, WRITE: TLSv1 Application Data, length = 182
[Raw write (bb)]: length = 213
0000: 17 03 01 00 D0 92 D5 AB   1C 3E 93 5F 45 C1 30 0D  .........>._E.0.
0010: 25 6A 5F E7 67 06 2B 2B   2A B4 1E E5 7E EE 6C 96  %j_.g.++*.....l.
0020: F5 A6 D0 19 37 3E 30 9D   99 6B 8F 75 E3 35 16 02  ....7>0..k.u.5..
0030: 90 4E 6D 22 A5 FE FF 37   E9 DF 7C 38 25 4B 05 6D  .Nm"...7...8%K.m
0040: CC 0A 8E 0E 3A 43 44 1F   23 83 D6 C4 4E B5 55 45  ....:CD.#...N.UE
0050: 58 7B 53 6E 03 2B 73 08   E6 EB 1E 49 75 B9 6E B4  X.Sn.+s....Iu.n.
0060: 2F 7D 6F 9A B2 B9 15 90   5F C7 82 67 98 39 AE 07  /.o....._..g.9..
0070: DC D0 53 B7 6C C2 C3 BE   7F 40 3F 73 DF 56 8A 3B  ..S.l....@?s.V.;
0080: D2 A2 EF F7 16 AC 4E F9   4C 75 F8 B1 A6 3B 95 F6  ......N.Lu...;..
0090: 92 73 AF 84 C0 52 6A 08   77 8C A7 B1 18 A4 71 B7  .s...Rj.w.....q.
00A0: B6 8C 56 19 7A A1 12 6C   E6 94 B0 84 C1 40 89 6D  ..V.z..l.....@.m
00B0: B7 D5 8C F5 57 80 3E 19   2D 11 24 B3 88 A2 F5 9C  ....W.>.-.$.....
00C0: FA 38 F9 D7 E8 1F 7B 7E   9F 72 68 BF 8C 82 D6 1E  .8.......rh.....
00D0: B7 3D 33 06 98                                     .=3..
[Raw read]: length = 5
0000: 16 03 01 00 20                                     ....
[Raw read]: length = 32
0000: AA AA D1 B2 BE D1 00 84   C1 93 BF 8A 9A 77 ED 76  .............w.v
0010: 53 BB E0 3B 4B DB E6 A6   92 00 D0 2E 05 B7 4F EF  S..;K.........O.
client-loop, READ: TLSv1 Handshake, length = 32
Padded plaintext after DECRYPTION:  len = 32
0000: 00 00 00 00 C5 5C 82 7D   4D E6 C8 3D F5 2D F6 9F  .....\..M..=.-..
0010: 64 B6 F9 DF 53 90 99 3F   07 07 07 07 07 07 07 07  d...S..?........
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: false
Is secure renegotiation: true
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
=>
{:opts {:sslengine #object[sun.security.ssl.SSLEngineImpl
                           0x7a21e0f7
                           "7a21e0f7[SSLEngine[hostname=null port=-1] TLS_RSA_WITH_AES_128_CBC_SHA]"],
        :timeout 4000,
        :method :get,
        :url "https://################################################################################"},
 :error #error{:cause "read timeout: 4000ms",
               :via [{:type org.httpkit.client.TimeoutException,
                      :message "read timeout: 4000ms",
                      :at [org.httpkit.client.HttpClient clearTimeout "HttpClient.java" 82]}],
               :trace [[org.httpkit.client.HttpClient clearTimeout "HttpClient.java" 82]
                       [org.httpkit.client.HttpClient run "HttpClient.java" 433]
                       [java.lang.Thread run "Thread.java" 745]]}}

实现这一目标的代码如下所示:

@(org.httpkit.client/get "https://################################################################################"
                         {:sslengine (build-ssl-engine (build-ssl-context {:keystore    "####################" :keystore-pass "########"
                                                                           :trust-store "####################" :trust-store-pass "########"
                                                                           :protocol    "TLSv1"}))
                          :timeout   4000})

辅助函数是:

(defn- setup-keystore ^KeyStore [file ^String password]
  (let [keystore (KeyStore/getInstance "JKS")]
    (with-open [inputstream (io/input-stream (io/resource file))]
      (.load keystore inputstream (.toCharArray password)))
    keystore))

(defn- key-managers [file ^String password]
  (let [manager-factory (KeyManagerFactory/getInstance (KeyManagerFactory/getDefaultAlgorithm))]
    (.init manager-factory (setup-keystore file password) (.toCharArray password))
    (.getKeyManagers manager-factory)))

(defn- trust-managers [file password]
  (let [manager-factory (TrustManagerFactory/getInstance (TrustManagerFactory/getDefaultAlgorithm))]
    (.init manager-factory (setup-keystore file password))
    (.getTrustManagers manager-factory)))

(defn build-ssl-context ^javax.net.ssl.SSLContext [{:keys [trust-store trust-store-pass keystore keystore-pass protocol]
                                                    :or   {protocol "TLS"}}]
  (let [key-managers (key-managers keystore keystore-pass)
        trust-managers (trust-managers trust-store trust-store-pass)
        ssl-context (SSLContext/getInstance protocol)]
    (.init ssl-context key-managers trust-managers nil)
    (SSLContext/setDefault ssl-context)
    ssl-context))

(defn build-ssl-engine [^SSLContext ssl-context]
  (doto (.createSSLEngine ssl-context)
    (.setUseClientMode true)
    (.setNeedClientAuth true)))

现在,我有一段类似但可行的Java代码。

%% Cached client session: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
[read] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C D2 53 C9 75   95 CC 0C E5 48 44 0E 7C  .....S.u....HD..
Padded plaintext before ENCRYPTION:  len = 240
0000: 47 45 54 20 ## ## ## ##   ## ## ## ## ## ## ## ##  GET /###########
0010: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0020: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0030: ## ## ## ## ## ## ## 20   48 54 54 50 2F 31 2E 31  ####### HTTP/1.1
0040: 0D 0A 55 73 65 72 2D 41   67 65 6E 74 3A 20 4A 61  ..User-Agent: Ja
0050: 76 61 2F 31 2E 38 2E 30   5F 31 31 32 0D 0A 48 6F  va/1.8.0_112..Ho
0060: 73 74 3A 20 ## ## ## ##   ## ## ## ## ## ## ## ##   st: ############
0070: ## ## ## ## ## ## ## ##   ## ## ## ## ## ## ## ##  ################
0080: ## 0D 0A 41 63 63 65 70   74 3A 20 74 65 78 74 2F  #..Accept: text/
0090: 68 74 6D 6C 2C 20 69 6D   61 67 65 2F 67 69 66 2C  html, image/gif,
00A0: 20 69 6D 61 67 65 2F 6A   70 65 67 2C 20 2A 3B 20   image/jpeg, *;
00B0: 71 3D 2E 32 2C 20 2A 2F   2A 3B 20 71 3D 2E 32 0D  q=.2, */*; q=.2.
00C0: 0A 43 6F 6E 6E 65 63 74   69 6F 6E 3A 20 6B 65 65  .Connection: kee
00D0: 70 2D 61 6C 69 76 65 0D   0A 0D 0A 82 2E C5 FA E3  p-alive.........
00E0: B7 BC 64 D0 AE 36 F6 FE   0A E7 38 D3 0A 39 76 00  ..d..6....8..9v.
main, WRITE: TLSv1 Application Data, length = 240
[Raw write]: length = 245
0000: 17 03 01 00 F0 34 28 EC   E7 31 92 96 43 E2 36 83  .....4(..1..C.6.
0010: AA 59 C1 CD EC 08 0D 8F   02 F0 3B 9D 3C 0A 65 73  .Y........;.<.es
0020: E7 FF 7E 08 5A 39 F2 16   AB 7F 02 CF B3 E4 A3 14  ....Z9..........
0030: 46 A4 EC 5A 1C B2 81 7B   CA 65 78 5E DF 19 48 40  F..Z.....ex^..H@
0040: 2F 2B 0F D4 BB 6D 09 52   C6 14 BC C8 D2 C5 41 8A  /+...m.R......A.
0050: 91 5F 90 53 A9 E3 15 21   2E 7C 8E 7B 2B E5 46 D6  ._.S...!....+.F.
0060: 9A 32 8F DF 35 49 6C 9A   DA CB 02 BA BE 83 1D D3  .2..5Il.........
0070: 39 D6 C5 F4 E9 27 E9 1D   A6 3F 0E BB C8 BE A0 6C  9....'...?.....l
0080: 32 97 BC FC 0C 04 68 8F   CC 6B DA AF F2 40 2D 56  2.....h..k...@-V
0090: 47 EC 92 F5 36 19 FF F6   B4 97 31 9F 5D F0 75 31  G...6.....1.].u1
00A0: 12 6A AD D2 FA 95 22 A5   F7 D8 20 36 D9 F9 69 87  .j...."... 6..i.
00B0: 67 05 AE 97 68 81 8D C4   38 AC 74 6A B7 DB 51 DE  g...h...8.tj..Q.
00C0: A2 65 86 F1 F4 F7 27 2C   3D 3C 85 BE DB 97 01 C7  .e....',=<......
00D0: 70 CE 19 FE D6 44 24 AE   F3 38 79 C2 B9 DE 71 EB  p....D$..8y...q.
00E0: 47 1A 3A 94 E8 3D F9 A0   DD 51 FD CE 01 84 E7 23  G.:..=...Q.....#
00F0: 8B F8 07 B2 2D                                     ....-
[Raw read]: length = 5
0000: 16 03 01 00 20                                     ....
[Raw read]: length = 32
0000: E6 11 77 2F 9D 87 61 F5   FE 99 0D 62 51 7E D0 93  ..w/..a....bQ...
0010: 55 D2 4B B3 E8 91 DC F6   69 D8 2E 2E BF F4 B2 0F  U.K.....i.......
main, READ: TLSv1 Handshake, length = 32
Padded plaintext after DECRYPTION:  len = 32
0000: 00 00 00 00 43 53 ED 57   2D 63 29 89 E3 20 9C DD  ....CS.W-c).. ..
0010: 17 58 AA E5 3D 13 BC BB   07 07 07 07 07 07 07 07  .X..=...........
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: false
Is secure renegotiation: true
*** HelloRequest (empty)
%% Client cached [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
%% Try resuming [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA] from port 57365
*** ClientHello, TLSv1

几乎相同的Java代码是:

public class Main {
    public static void main(String[] args) {
        try {
            String keystore = "####################";
            String keystorePassword = "########";
            String trustStore = "####################";
            String trustStorePassword = "########";
            String protocol = "TLSv1";

            buildSslContext(keystore, keystorePassword, trustStore, trustStorePassword, protocol);

            URL url = new URL("https://################################################################################");
            HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection();
            httpsCon.setRequestMethod("GET");
            String responseMessage = httpsCon.getResponseMessage();

            System.out.printf("Response Message is %s\n", responseMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void buildSslContext(String keystore, String keystorePassword, String trustStore, String trustStorePassword, String protocol) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
        SSLContext sslContext = SSLContext.getInstance(protocol);
        KeyManager[] keyManagers = keyManagers(keystore, keystorePassword);
        TrustManager[] trustManagers = trustManagers(trustStore, trustStorePassword);
        sslContext.init(keyManagers, trustManagers, null);
        SSLContext.setDefault(sslContext);
    }

    private static TrustManager[] trustManagers(String file, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        KeyStore trustStore = setupKeystore(file, password);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
        return trustManagerFactory.getTrustManagers();
    }

    private static KeyManager[] keyManagers(String file, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
        KeyStore keyStore = setupKeystore(file, password);
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password.toCharArray());
        return keyManagerFactory.getKeyManagers();
    }

    private static KeyStore setupKeystore(String file, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        InputStream trustStoreUrl = new FileInputStream(file);
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(trustStoreUrl, password.toCharArray());
        return trustStore;
    }
}

最大的区别在于Clojure代码正在创建自己的SSLEngine,而Java代码似乎只是将SSLContext设置为默认代码。似乎Java从该Context创建了一个SSLEngine,而http-kit希望您提供它。请注意,我正在将其修复为TLSv1,因为如果我使用TLSv1.1或v1.2,则此IIS 7会终止连接。

有什么想法和/或如何修复它?

1 个答案:

答案 0 :(得分:1)

我将在前面加上“我不是一个Clojure人”,但我认为您需要在SSLContext代码中提供自己的http-kit。有一个很好的blog post here讨论了如何使用名为less-awful-ssl的Clojure库来生成SSLContext并将其作为opts地图提供给http-kit请求方法(.createSSLEngine ctx)将为您提供正确的SSLEngine

为了记录,当提供的密钥库没有RSA / DSA密钥或证书是证书时,我经常看到(即使在纯Java中)所有名义上支持的套件的“忽略不支持的密码套件”消息无效(即日期已过期等)。