アンドロイド4.4 ALPN支え問い合わせ

前述のAndroidであるURLConnection彼らはサポートしていないためALPNないようにサポートします、HTTP/2そして、OkHttp 2.5上記の、それはサポートしているALPNHTTP/2、これらに限定され、プラットフォームの要件のためにAndroid 5.0上記。違いはそれのプラットフォームになるだろう、なぜそう、それが原因、あなたのお母さんの度合いに言われたopenssl、必要openssl 1.0.2以上だけ支援よりもTLS、私の手Android 4.4ではopenssl 1.0.1eその理由、レッツ・スタートではありませんOkHttp開始します。

まず、関連するインタフェースの存在?

ではOkHttp、ソースコードokhttp3.internal.platform.Platform.javaのコメントで、これは言うことがあります。

プラットフォーム固有の機能へのアクセス。... ALPN(アプリケーション層プロトコルのネゴシエーション)

アンドロイド5.0以上でサポートされています。APIには、Android 4.4に存在していたが、その実装が不安定でした。

(JettyALPNブートライブラリ経由)OpenJDKの7と8でサポートされています。

SSLParametersとのSSLSocket機能を通じてOpenJDKの9でサポートされています。...

意味が言うようですが、この部分の内側にはありますが、実装が不安定になりますか?不安定な、それはいくつかのことでした。見て続行API4.4Platform.java

/** Attempt to match the host runtime to a capable Platform implementation. */
private static Platform findPlatform() {
    Platform android = AndroidPlatform.buildIfSupported();
    if (android != null) {
        return android;
    }

    Platform jdk9 = Jdk9Platform.buildIfSupported();

    if (jdk9 != null) {
        return jdk9;
    }

    Platform jdkWithJettyBoot = JdkWithJettyBootPlatform.buildIfSupported();

    if (jdkWithJettyBoot != null) {
        return jdkWithJettyBoot;
    }

    // Probably an Oracle JDK like OpenJDK.
    return new Platform();
}

見ることができますAndroid適応プラットフォームは経由でAndroidPlatform.javaで、実現buildIfSupported()方法:

public static Platform buildIfSupported() {
    // Attempt to find Android 2.3+ APIs.
    try {
        Class<?> sslParametersClass;
        try {
            sslParametersClass = Class.forName("com.android.org.conscrypt.SSLParametersImpl");
        } catch (ClassNotFoundException e) {
            // Older platform before being unbundled.
            sslParametersClass = Class.forName("org.apache.harmony.xnet.provider.jsse.SSLParametersImpl");
        }

        OptionalMethod<Socket> setUseSessionTickets = new OptionalMethod<>(null, "setUseSessionTickets", boolean.class);
        OptionalMethod<Socket> setHostname = new OptionalMethod<>(null, "setHostname", String.class);
        OptionalMethod<Socket> getAlpnSelectedProtocol = null;
        OptionalMethod<Socket> setAlpnProtocols = null;

        // Attempt to find Android 5.0+ APIs.
        try {
            Class.forName("android.net.Network"); // Arbitrary class added in Android 5.0.
            getAlpnSelectedProtocol = new OptionalMethod<>(byte[].class, "getAlpnSelectedProtocol");
            setAlpnProtocols = new OptionalMethod<>(null, "setAlpnProtocols", byte[].class);
        } catch (ClassNotFoundException ignored) {
        }

        return new AndroidPlatform(sslParametersClass, setUseSessionTickets, setHostname, getAlpnSelectedProtocol, setAlpnProtocols);
    } catch (ClassNotFoundException ignored) {
        // This isn't an Android runtime.
    }

    return null;
}

つまり、なかったOkHttpを見てAndroid 5.0+、既存のクラスandroid.net.Network.javaキーであることを特徴と指定されたクラスからの反射の二つの方法、つつ、プラットフォームのバージョンを決定するために、getAlpnSelectedProtocol()ここでは、戻り値任意キャッシングメソッド名であるパラメータそのため、指定されたクラスは、それは何ですか、決して心は、我々は置くことを、反射によってバックと呼ばれる上Class.forName("android.net.Network");で実行するために彼を強制的に、この行はコメントアウトを4.4先に言ったかどうかを確認するために、上、API存在しているが、不安定な、探しているgetAlpnSelectedProtocol()特定のローカルコール:

@Override public String getSelectedProtocol(SSLSocket socket) {
    if (getAlpnSelectedProtocol == null) return null;
    if (!getAlpnSelectedProtocol.isSupported(socket)) return null;
    byte[] alpnResult = (byte[]) getAlpnSelectedProtocol.invokeWithoutCheckedException(socket);
    return alpnResult != null ? new String(alpnResult, Util.UTF_8) : null;
}

isSupported()これは、反射することによって決定されるtarget適切な方法の有無を。

/**
* Returns true if the method exists on the supplied {@code target}.
*/
public boolean isSupported(T target) {
    return getMethod(target.getClass()) != null;
}

debugモードは、ここで我々が見つかりました:

上記された4.4演算結果は、得られた結果のみが、によって行うことができるalpnResultのに対して、空である5.0機械、文字列の解析値が存在し、それはh2

これは、公式文書が言ったように、思わgetAlpnSelectedProtocol()インターフェイス4.4そこには、が、空を返す、ためので、正しい結果を得ることはありませんでしたかunstable

第二に、なぜgetAlpnSelectedProtocolは空気に戻りますか?

次は、我々は、このインターフェイスを保持するクラスを参照してください。debugあなたが見ることができます:

もともとと呼ばれていたcom.android.org.conscrypt.OpenSSLSocketImplWrapper、残念ながら、そこに、クラスのソースコードは、クラスAndroid SDKのダウンロードのための開発キットが利用できるsources見つけることができません。それはここにあります:

所以我们要找conscrypt.jar的源码, OpenSSLSocketImplWrapper其实是OpenSSLSocketImpl的子类:https://android.googlesource.com/platform/external/conscrypt/+/android-5.0.0_r1/src/main/java/org/conscrypt/OpenSSLSocketImpl.java

还有4.4的,我可是翻了很久才找到的: https://android.googlesource.com/platform/external/conscrypt/+/e75878c72b717696d7e4f6cc1052f1cdaca3bda8/src/main/java/org/conscrypt/OpenSSLSocketImpl.java

两者的实现差不多,都包含:

/**
* Returns the protocol agreed upon by client and server, or {@code null} if
* no protocol was agreed upon.
*/
public byte[] getAlpnSelectedProtocol() {
    return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
}

从注释可以看到,是需要客户端和服务端都同意ALPN,才会有返回值,具体实现,是个Native方法:

/**
* Returns the selected ALPN protocol. If the server did not select a
* protocol, {@code null} will be returned.
*/
public static native byte[] SSL_get0_alpn_selected(long sslPointer);

那这个native方法指向哪里呢,没错,它指向了openssl

openssl-1.0.2j 下载:https://www.openssl.org/source/openssl-1.0.2j.tar.gz

openssl-1.0.1e 下载:https://www.openssl.org/source/old/1.0.1/openssl-1.0.1e.tar.gz

openssl-1.0.2jincludeopensslssl.h中可以找到:

void大专栏  Android 4.4 ALPN支持性探究an> SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
                            unsigned *len);

而在openssl-1.0.1eincludeopensslssl.h中:

我勒个去,Unable to find… 原来这就是unstable的原因啊!!!

三、替换openssl

既然Android 4.4openssl-1.0.1e缺少SSL_get0_alpn_selected ,那我们换成openssl-1.0.2j试试。

编译过程在这里:Android Openssl交叉编译

另外,Openssl官方wiki有个Android环境编译教程: https://wiki.openssl.org/index.php/Android#Acquire_the_Required_Files

可惜编译出来的没法使用:

CANNOT LINK EXECUTABLE: could not load library "libandroid_runtime.so" needed by "app_process"; caused by could not load library "libcrypto.so" needed by "libandroid_runtime.so"; caused by "libcrypto.so" has bad ELF magic

或者caused by "libssl.so" has bad ELF magic

替换完成后debuggetAlpnSelectedProtocol()

alpnResult还是null。。。最终协商结果还是HTTP/1.1:

四、这到底是为什么

从前面getAlpnSelectedProtocol()方法的注释说明:

/**
* Returns the protocol agreed upon by client and server, or {@code null} if
* no protocol was agreed upon.
*/
public byte[] getAlpnSelectedProtocol() {
    return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
}

还有SSL_get0_alpn_selected的注释说明:

/*
* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from
* |ssl|. On return it sets |*data| to point to |*len| bytes of protocol name
* (not including the leading length-prefix byte). If the server didn't
* respond with a negotiated protocol then |*len| will be zero.
*/
void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len)
{
    *data = NULL;
    if (ssl->s3)
        *data = ssl->s3->alpn_selected;
    if (*data == NULL)
        *len = 0;
    else
        *len = ssl->s3->alpn_selected_len;
}

可以看到,是需要客户端和服务端都同意ALPN协商,而SSL_get0_alpn_selected只是从协商结果中判断是否能使用HTTP/2,难道不是openssl的问题?那问题出在哪呢

五、TLS链接建立和ALPN协商过程

看来有必要从头了解一下HTTP/2请求过程,我们在浏览器上访问一下HTTP/2网站,抓包看看:

首先,ClientServer发送一个ClientHello消息,说明它支持的密码算法列表、压缩方法及最高协议版本,以及稍后将被使用的随机数:

TLSv1.2中,会有个扩展字段Extension,通过ALPN扩展列出了自己支持的各种应用层协议,比如h2http/1.1

然后服务端会在Server Hello中选取所支持的加密算法和密钥大小,以及支持的协议,如果服务端支持HTTP/2,指定ALPN的协商结果为h2就可以了;如果服务端不支持HTTP/2,就会从客户端的 ALPN支持列表中选一个自己可以支持的。比如下面这图,服务端选择了降级成HTTP/1.1

这就是TLS链接建立ALPN协商的过程。

其中ALPN被包含了在TLS v1.22Extension字段中,查看TLS wiki,在1.2以前的版本是没有Extension字段的,看来问题的关键在于TLS的版本。

六、TLS支持

我们来看看Android所支持的TLS版本:https://developer.android.com/reference/javax/net/ssl/SSLSocket.html

好吧,API 164.1)就提供支持,但API 205.0)才默认打开。。。

那能不能手动打开呢,先看一下执行结果:

5.0:在初始化的ssLSocketFactory的时候,sslParameters里面enabledProtocls列表包含了TLSv1.2

4.4:只有TLSv1

再看初始化sslParameters的源码:

5.0下: com.android.org.conscrypt.SSLParametersImpl.java:

protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms, SecureRandom sr, ClientSessionContext clientSessionContext, ServerSessionContext serverSessionContext) throws KeyManagementException {
    this.serverSessionContext = serverSessionContext;
    this.clientSessionContext = clientSessionContext;
    // initialize key managers
    if (kms == null) {
        x509KeyManager = getDefaultX509KeyManager();
        // There's no default PSK key manager
        pskKeyManager = null;
    } else {
        x509KeyManager = findFirstX509KeyManager(kms);
        pskKeyManager = findFirstPSKKeyManager(kms);
    }
    // initialize x509TrustManager
    if (tms == null) {
        x509TrustManager = getDefaultX509TrustManager();
    } else {
        x509TrustManager = findFirstX509TrustManager(tms);
    }
    // initialize secure random
    // We simply use the SecureRandom passed in by the caller. If it's
    // null, we don't replace it by a new instance. The native code below
    // then directly accesses /dev/urandom. Not the most elegant solution,
    // but faster than going through the SecureRandom object.
    secureRandom = sr;
    // initialize the list of cipher suites and protocols enabled by default
    enabledProtocols = getDefaultProtocols();
    boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
    boolean pskCipherSuitesNeeded = pskKeyManager != null;
    enabledCipherSuites = getDefaultCipherSuites(x509CipherSuitesNeeded, pskCipherSuitesNeeded);
}


private static String[] getDefaultProtocols() {
    return NativeCrypto.DEFAULT_PROTOCOLS.clone();
}

com.android.org.conscrypt. NativeCrypto.java:

public static final String[] DEFAULT_PROTOCOLS = new String[] {
    SUPPORTED_PROTOCOL_SSLV3,
    SUPPORTED_PROTOCOL_TLSV1,
    SUPPORTED_PROTOCOL_TLSV1_1,
    SUPPORTED_PROTOCOL_TLSV1_2,
};

4.4下:

com.android.org.conscrypt.SSLParametersImpl.java:

// protocols available for SSL connection
private String[] enabledProtocols = ProtocolVersion.supportedProtocols;

com.android.org.conscrypt. ProtocolVersion.java:

/**
* Protocols supported by this provider implementation
*/
public static final String[] supportedProtocols = new String[] { "TLSv1", "SSLv3" };

所以不管是4.4还是5.0,支持的TLS版本都被写死在了外部扩展库conscrypt.jar里面。

所以看来需要重新编译Android 4.4的源码才能解决了。。。

おすすめ

転載: www.cnblogs.com/liuzhongrong/p/11961136.html