3

アイテムにいくつかのコンテンツ (TextViews、ImageView...) を含む ListView があります。Nostra のUILを使用してアイテムの画像を読み込んでいますが、一部の画像を読み込めません。これは、私が呼び出すときに得られるものですLog.v(String.valueOf(failReason.getCause());:

11-16 23:52:20.447: V/javax.net.ssl.SSLHandshakeException: Handshake failed(17467): failz
11-16 23:52:20.657: V/NativeCrypto(17467): SSL handshake aborted: ssl=0x15fd758: Failure in SSL library, usually a protocol error
11-16 23:52:20.657: V/NativeCrypto(17467): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:762 0x4c2ed485:0x00000000)
11-16 23:52:21.207: V/NativeCrypto(17467): SSL handshake aborted: ssl=0x1562468: Failure in SSL library, usually a protocol error
11-16 23:52:21.207: V/NativeCrypto(17467): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:762 0x4c2ed485:0x00000000)

なぜこの問題があるのか​​、どうすれば解決できるのかわかりませんか?

これは、ロードされない 1 つの画像の例です。

http://bigparty.cz/photos/headlinefoto/13.jpg

(エラー全体を含むログを添付できます-UILが自動的にログに記録するエラー)

4

3 に答える 3

9

私が正しければ、証明書を作成し、署名してアプリに含める必要があります。または、サーバー構成を変更します (詳細はこちら)。

そうしないと、アプリ内のすべてのハンドシェイクを信頼できます。これは最善の方法ではありませんが、実装時には非常に役立ちます。

このクラスをプロジェクトに含める

public class SSLCertificateHandler {

protected static final String TAG = "NukeSSLCerts";

/**
 * Enables https connections
 */
public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] myTrustedAnchors = new X509Certificate[0];
                return myTrustedAnchors;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
    } catch (Exception e) {
    }
}

}

あなたの を拡張し、あなたApplicationの 'nuke' 関数を呼び出しますonCreate

public class YOURApplication extends Application {

@Override
public void onCreate() {
    super.onCreate();

    //...

    // trust all SSL -> HTTPS connection
    SSLCertificateHandler.nuke();
}

SOでこのコードを見つけましたが、現時点ではリンクが見つかりません....

于 2014-11-25T11:57:05.200 に答える
2

longilong で述べたように、すべての証明書を信頼することは最善の方法ではありません。実際、これは実稼働環境では非常に悪いアプローチです。基本的に SSL の効果を無効にし、中間者攻撃を受けやすくするからです。

問題は、一部の (ほとんどの?) サービス プロバイダーが SSLv3 サポートを無効にしたPOODLE SSL3 バグに要約されます。Build.VERSION.SDK_INT >= 9 && Build.VERSION.SDK_INT <= 20.

詳細については、 Android イシュー トラッカーに提出されたイシューがあり、ソリューションは、唯一のフォールバック プロトコルとして SSLv3 を設定することを拒否するカスタム SSLFactory を提供することで構成されています。このソリューションの功績は認めませんが、Lollipop より前のすべてのバージョンで現在使用しているものです。

/**
 * An {@link javax.net.ssl.SSLSocket} that doesn't allow {@code SSLv3} only connections
 * <p>fixes https://github.com/koush/ion/issues/386</p>
 */
private static class NoSSLv3SSLSocket extends DelegateSSLSocket {

private NoSSLv3SSLSocket(SSLSocket delegate) {
    super(delegate);

    String canonicalName = delegate.getClass().getCanonicalName();
    if (!canonicalName.equals("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl")){
        // try replicate the code from HttpConnection.setupSecureSocket()
        try {
            Method msetUseSessionTickets = delegate.getClass().getMethod("setUseSessionTickets", boolean.class);
            if (null != msetUseSessionTickets) {
                msetUseSessionTickets.invoke(delegate, true);
            }
        } catch (NoSuchMethodException ignored) {
        } catch (InvocationTargetException ignored) {
        } catch (IllegalAccessException ignored) {
        }
    }
}

@Override
public void setEnabledProtocols(String[] protocols) {
    if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
        // no way jose
        // see issue https://code.google.com/p/android/issues/detail?id=78187
        List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
        if (enabledProtocols.size() > 1) {
            enabledProtocols.remove("SSLv3");
        } else {
            LogManager.getLogger().w("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
        }
        protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
    }
    super.setEnabledProtocols(protocols);
}
}


/**
 * {@link javax.net.ssl.SSLSocketFactory} that doesn't allow {@code SSLv3} only connections
 */
private static class NoSSLv3Factory extends SSLSocketFactory {
private final SSLSocketFactory delegate;

private NoSSLv3Factory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

private static Socket makeSocketSafe(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new NoSSLv3SSLSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
}

static {
HttpsURLConnection.setDefaultSSLSocketFactory(new NoSSLv3Factory());
}
于 2014-12-17T10:38:14.993 に答える
2

サーバー側で中間 CA 証明書が不足している可能性があります。

これを読んでみてください

ほとんどのパブリック CA は、サーバー証明書に直接署名しません。代わりに、ルート CA と呼ばれるメイン CA 証明書を使用して、中間 CA に署名します。これにより、ルート CA をオフラインで保存して侵害のリスクを軽減できます。ただし、Android などのオペレーティング システムは通常、ルート CA のみを直接信頼するため、中間 CA によって署名されたサーバー証明書とルート CA を認識している証明書検証者との間に短い信頼のギャップが残ります。これを解決するために、サーバーは SSL ハンドシェーク中に証明書だけをクライアントに送信するのではなく、信頼されたルート CA に到達するために必要な中間を介してサーバー CA から一連の証明書を送信します。

これが実際にどのように見えるかを確認するために、openssl s_client コマンドで表示される mail.google.com 証明書チェーンを次に示します。

$ openssl s_client -connect mail.google.com:443

証明書チェーン 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mail.google.com i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN= Thawte SGC CA 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA

i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority

これは、サーバーが、中間 CA である Thawte SGC CA によって発行された mail.google.com の証明書と、Verisign CA によって発行された Thawte SGC CA の 2 番目の証明書を送信することを示しています。Verisign CA は、によって信頼されているプラ​​イマリ CA です。アンドロイド。

ただし、必要な中間 CA を含めないようにサーバーを構成することは珍しくありません。たとえば、Android ブラウザーでエラーを引き起こし、Android アプリで例外を引き起こす可能性のあるサーバーを次に示します。

$ openssl s_client -connect egov.uscis.gov:443

証明書チェーン 0 s:/C=US/ST=District Of Columbia/L=Washington/O=US Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov

i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=利用規約https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3

ここで興味深いのは、ほとんどのデスクトップ ブラウザーでこのサーバーにアクセスしても、完全に不明な CA や自己署名サーバー証明書のようなエラーが発生しないことです。これは、ほとんどのデスクトップ ブラウザが、信頼できる中間 CA を時間の経過とともにキャッシュするためです。ブラウザーが 1 つのサイトにアクセスして中間 CA について学習すると、次回は中間 CA を証明書チェーンに含める必要はありません。

一部のサイトでは、リソースを提供するために使用されるセカンダリ Web サーバーに対して意図的にこれを行っています。たとえば、メインの HTML ページは完全な証明書チェーンを持つサーバーによって提供されますが、おそらく帯域幅を節約するために、画像、CSS、または JavaScript などのリソース用のサーバーには CA が含まれていません。残念ながら、これらのサーバーが、Android アプリから呼び出そうとする Web サービスを提供している場合がありますが、これはそれほど寛容ではありません。

この問題を解決するには、次の 2 つの方法があります。

サーバー チェーンに中間 CA を含めるようにサーバーを構成します。ほとんどの CA は、すべての一般的な Web サーバーに対してこれを行う方法に関するドキュメントを提供しています。少なくとも Android 4.2 まではデフォルトの Android ブラウザーでサイトを動作させる必要がある場合は、これが唯一の方法です。または、前の 2 つのセクションで行ったように、中間 CA を他の不明な CA と同様に扱い、それを直接信頼する TrustManager を作成します。

于 2014-11-26T08:26:32.250 に答える