เชื่อถือใบรับรองทั้งหมดโดยใช้ HttpClient ผ่าน HTTPS


393

โพสต์เมื่อเร็ว ๆ นี้คำถามเกี่ยวกับHttpClientมากกว่า Https ( พบที่นี่ ) ฉันมีความคืบหน้าบ้าง แต่ฉันพบปัญหาใหม่ เช่นเดียวกับปัญหาล่าสุดของฉันฉันไม่สามารถหาตัวอย่างที่เหมาะกับฉันได้ โดยทั่วไปฉันต้องการให้ลูกค้าของฉันยอมรับใบรับรองใด ๆ (เพราะฉันเคยชี้ไปที่เซิร์ฟเวอร์เดียว) แต่ฉันได้รับjavax.net.ssl.SSLException: Not trusted server certificate exception.

ดังนั้นนี่คือสิ่งที่ฉันมี:


    public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS {

        HttpPost post = new HttpPost(new URI(PROD_URL));
        post.setEntity(new StringEntity(BODY));

        KeyStore trusted = KeyStore.getInstance("BKS");
        trusted.load(null, "".toCharArray());
        SSLSocketFactory sslf = new SSLSocketFactory(trusted);
        sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme ("https", sslf, 443));
        SingleClientConnManager cm = new SingleClientConnManager(post.getParams(),
                schemeRegistry);

        HttpClient client = new DefaultHttpClient(cm, post.getParams());
        HttpResponse result = client.execute(post);
    }

และนี่คือข้อผิดพลาดที่ฉันได้รับ:

    W/System.err(  901): javax.net.ssl.SSLException: Not trusted server certificate 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360) 
    W/System.err(  901):    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:321) 
    W/System.err(  901):    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) 
    W/System.err(  901):    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:129) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.java:77) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.java:49) 
    W/System.err(  901): Caused by: java.security.cert.CertificateException: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:157) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:355) 
    W/System.err(  901):    ... 12 more 
    W/System.err(  901): Caused by: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:645) 
    W/System.err(  901):    at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.java:134) 
    W/System.err(  901):    at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.java:226)W/System.err(  901):     at org.apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.java:263) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:190) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:216) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:107) 
    W/System.err(  901):    ... 2 more

17
ฉันต้องทำสิ่งนี้เพื่อการใช้งานภายใน ฉันหวังว่าคุณจะไม่ปล่อยให้ผู้ใช้นอก บริษัท ของคุณใช้แอพของคุณเนื่องจากคุณเปิดมันขึ้นมาเพื่อโจมตีคนตรงกลาง อย่างไรก็ตามฉันต้องทำสิ่งนี้ชั่วคราวสำหรับการทดสอบบางอย่างจนกว่าฉันจะได้รับใบรับรองจริงในมือ ..... หวังว่าคุณจะทำมันด้วยเหตุผลแบบเดียวกันหรือว่าแอพนี้ใช้ภายในเท่านั้น
Dean Hiller

ฉันลองวิธีแก้ปัญหาเหล่านี้บนไคลเอนต์ http apache 4.3 แต่พวกเขาส่วนใหญ่เลิกใช้แล้ว ที่นี่ไม่ได้แก้ปัญหาคือ: stackoverflow.com/a/18941950/2039471
Alexander Chzhen

Java 1.6 ไม่มีการสนับสนุน SNI ซึ่งเป็นปัญหาในสถานการณ์เหล่านี้ - หากคุณสร้างคำขอไม่ถูกต้องคุณอาจได้รับใบรับรองที่ไม่ตรงกับคำขอ ดูproblems.apache.org/jira/browse/HTTPCLIENT-1119
Bron Davies

2
คำถามนี้ถูกอ้างถึงในประมวลกฎหมายที่อันตรายที่สุดในโลกเป็นตัวอย่างของการใช้เหตุผลที่ผิดพลาด (รายงานการวิจัย: cs.utexas.edu/~shmat/shmat_ccs12.pdf )
mk_

คำตอบ:


421

หมายเหตุ: อย่าใช้สิ่งนี้ในรหัสการผลิตที่คุณจะใช้ในเครือข่ายที่คุณไม่เชื่อถือ โดยเฉพาะอย่างยิ่งสิ่งใดก็ตามที่เกิดขึ้นบนอินเทอร์เน็ตสาธารณะ

คำถามของคุณเป็นเพียงสิ่งที่ฉันอยากรู้ หลังจากที่ฉันทำการค้นหาบางอย่างข้อสรุปมีดังนี้

ในวิธี HttpClient คุณควรสร้างคลาสที่กำหนดเองจาก org.apache.http.conn.ssl.SSLSocketFactory ไม่ใช่ org.apache.http.conn.ssl.SSLSocketFactory หนึ่งตัว ปมบางอย่างสามารถพบได้ในโพสต์นี้การจัดการที่กำหนดเอง SSL หยุดทำงานใน Android 2.2 FroYo

ตัวอย่างเช่น ...

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

และใช้คลาสนี้ในขณะที่สร้างอินสแตนซ์ของ HttpClient

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

BTW ลิงค์ด้านล่างนี้สำหรับผู้ที่กำลังมองหาโซลูชัน HttpURLConnection Https Connection Android

ฉันได้ทดสอบวิธีแก้ปัญหาทั้งสองแบบด้านบนบน froyo และพวกเขาทั้งหมดทำงานเหมือนมีเสน่ห์ในกรณีของฉัน ในที่สุดการใช้ HttpURLConnection อาจประสบปัญหาการเปลี่ยนเส้นทาง แต่สิ่งนี้อยู่นอกเหนือหัวข้อ

หมายเหตุ: ก่อนที่คุณจะตัดสินใจที่จะเชื่อถือใบรับรองทั้งหมดคุณควรจะรู้ว่าไซต์นั้นดีและจะไม่เป็นอันตรายต่อผู้ใช้ปลายทาง

แน่นอนความเสี่ยงที่คุณควรได้รับการพิจารณาอย่างรอบคอบรวมถึงผลกระทบของไซต์จำลองของแฮ็กเกอร์ที่กล่าวถึงในความคิดเห็นต่อไปนี้ที่ฉันชื่นชมอย่างลึกซึ้ง ในบางสถานการณ์แม้ว่าอาจจะยากที่จะดูแลใบรับรองทั้งหมด แต่คุณควรทราบข้อเสียเปรียบโดยนัยที่จะเชื่อถือได้ทั้งหมด


150
คำตอบนี้อาจจะทราบว่าไว้วางใจใบรับรองทั้งหมดไม่ปลอดภัยอย่างน่ากลัวและเป็นโมฆะวัตถุประสงค์ทั้งหมดของ SSL ...
yanokwa

22
@sweeney - ยกเว้นว่ามันไม่รับประกันว่าคุณกำลังพูดคุยกับเซิร์ฟเวอร์ที่คุณคิดว่าคุณเป็น หากใครบางคนทำให้เซิร์ฟเวอร์ DNS ยุ่งเหยิงคุณอาจกำลังสื่อสารคีย์เข้ารหัสกับเซิร์ฟเวอร์ของแฮ็กเกอร์
Richard Szalay

12
@sweeney กล่าวอีกนัยหนึ่งคุณต้องรับผิดชอบต่อการโจมตีจากคนกลาง คุณควรทราบว่ารหัสนั้นไม่ตรงตามข้อกำหนด: ตรวจสอบ Javadoc getAcceptedIssuers()ไม่อนุญาตให้ส่งคืน null
มาร์ควิสแห่ง Lorne

25
-1 เพราะเป็นความคิดที่ดีที่จะยอมรับใบรับรองทั้งหมด มันแย่มากที่มีบล็อกและแบบฝึกหัดมากมายที่แนะนำนักพัฒนา Java อย่างมีความสุขตลอดเส้นทางของการทำสิ่งที่ผิด
ทิมเบนเดอร์

57
+1 เพราะฉันต้องการวิธีแก้ปัญหาอย่างรวดเร็วสำหรับการดีบักเท่านั้น ฉันจะไม่ใช้สิ่งนี้ในการผลิตเนื่องจากปัญหาด้านความปลอดภัยที่ผู้อื่นได้กล่าวถึง แต่นี่คือสิ่งที่ฉันต้องการสำหรับการทดสอบ ขอบคุณ!
Danation

495

โดยทั่วไปคุณมีวิธีแก้ปัญหาที่เป็นไปได้สี่ข้อเพื่อแก้ไขข้อยกเว้น "ไม่น่าเชื่อถือ" บน Android โดยใช้ httpclient:

  1. เชื่อถือใบรับรองทั้งหมด อย่าทำสิ่งนี้นอกจากว่าคุณรู้ว่ากำลังทำอะไรอยู่
  2. สร้าง SSLSocketFactory ที่กำหนดเองที่เชื่อถือได้เฉพาะใบรับรองของคุณ สิ่งนี้ทำงานได้ตราบใดที่คุณรู้ว่าเซิร์ฟเวอร์ใดที่คุณกำลังจะเชื่อมต่อ แต่ทันทีที่คุณต้องเชื่อมต่อกับเซิร์ฟเวอร์ใหม่ด้วยใบรับรอง SSL อื่นคุณจะต้องอัปเดตแอปของคุณ
  3. สร้างไฟล์ที่เก็บคีย์ที่มีใบรับรอง "รายการหลัก" ของ Android จากนั้นเพิ่มของคุณเอง หากใบรับรองใดก็ตามหมดอายุลงตามถนนคุณมีหน้าที่รับผิดชอบในการอัปเดตพวกเขาในแอปของคุณ ฉันไม่สามารถคิดเหตุผลในการทำเช่นนี้
  4. สร้าง SSLSocketFactory แบบกำหนดเองที่ใช้ KeyStore ในตัว แต่กลับใช้ KeyStore สำรองสำหรับสิ่งใดก็ตามที่ล้มเหลวในการตรวจสอบด้วยค่าเริ่มต้น

คำตอบนี้ใช้โซลูชัน # 4 ซึ่งดูเหมือนว่าฉันจะแข็งแกร่งที่สุด

วิธีแก้ปัญหาคือการใช้ SSLSocketFactory ที่สามารถรับ KeyStores ได้หลายตัวช่วยให้คุณสามารถจัดหา KeyStore ของคุณเองด้วยใบรับรองของคุณเอง สิ่งนี้ช่วยให้คุณโหลดใบรับรองระดับบนสุดเพิ่มเติมเช่น Thawte ที่อาจหายไปในอุปกรณ์ Android บางรุ่น นอกจากนี้ยังช่วยให้คุณโหลดใบรับรองที่ลงนามด้วยตนเองได้เช่นกัน มันจะใช้ใบรับรองอุปกรณ์เริ่มต้นในตัวก่อนและถอยกลับไปใช้ใบรับรองเพิ่มเติมของคุณตามความจำเป็นเท่านั้น

ก่อนอื่นคุณต้องกำหนดว่าคุณขาดใบรับรองใดใน KeyStore รันคำสั่งต่อไปนี้:

openssl s_client -connect www.yourserver.com:443

และคุณจะเห็นผลลัพธ์ดังนี้:

Certificate chain
 0 s:/O=www.yourserver.com/OU=Go to 
   https://www.thawte.com/repository/index.html/OU=Thawte SSL123 
   certificate/OU=Domain Validated/CN=www.yourserver.com
   i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
 1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
   i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 
   2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA

อย่างที่คุณเห็นใบรับรองหลักของเรามาจาก Thawte ไปที่เว็บไซต์ของผู้ให้บริการของคุณและค้นหาใบรับรองที่เกี่ยวข้อง สำหรับเรามันอยู่ที่นี่และคุณจะเห็นว่าสิ่งที่เราต้องการคือหนึ่งลิขสิทธิ์ 2006

หากคุณใช้ใบรับรองแบบลงนามด้วยตนเองคุณไม่จำเป็นต้องทำตามขั้นตอนก่อนหน้าเนื่องจากคุณมีใบรับรองการลงนามแล้ว

จากนั้นสร้างไฟล์ที่เก็บคีย์ที่มีใบรับรองการลงนามที่หายไป Crazybob มีรายละเอียดวิธีการทำเช่นนี้บน Androidแต่ความคิดคือการทำต่อไปนี้:

ถ้าคุณไม่ได้มันแล้วดาวน์โหลดห้องสมุดให้บริการเด้งปราสาทจาก: http://www.bouncycastle.org/latest_releases.html สิ่งนี้จะอยู่ใน classpath ของคุณด้านล่าง

รันคำสั่งเพื่อแยกใบรับรองจากเซิร์ฟเวอร์และสร้างไฟล์ pem ในกรณีนี้ mycert.pem

echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
 sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

จากนั้นรันคำสั่งต่อไปนี้เพื่อสร้างที่เก็บคีย์

export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
    rm $CERTSTORE || exit 1
fi
keytool \
      -import \
      -v \
      -trustcacerts \
      -alias 0 \
      -file <(openssl x509 -in mycert.pem) \
      -keystore $CERTSTORE \
      -storetype BKS \
      -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
      -providerpath /path/to/bouncycastle/bcprov-jdk15on-155.jar \
      -storepass some-password

res/raw/mystore.bksคุณจะสังเกตเห็นว่าสถานที่ดังกล่าวข้างต้นสคริปต์ผลใน ตอนนี้คุณมีไฟล์ที่คุณจะโหลดลงในแอพ Android ที่มีใบรับรองหายไป

ในการทำสิ่งนี้ลงทะเบียน SSLSocketFactory ของคุณสำหรับแบบแผน SSL:

final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443));

// and then however you create your connection manager, I use ThreadSafeClientConnManager
final HttpParams params = new BasicHttpParams();
...
final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);

วิธีสร้าง SSLSocketFactory ของคุณ:

protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() {
    try {
        final KeyStore ks = KeyStore.getInstance("BKS");

        // the bks file we generated above
        final InputStream in = context.getResources().openRawResource( R.raw.mystore);  
        try {
            // don't forget to put the password used above in strings.xml/mystore_password
            ks.load(in, context.getString( R.string.mystore_password ).toCharArray());
        } finally {
            in.close();
        }

        return new AdditionalKeyStoresSSLSocketFactory(ks);

    } catch( Exception e ) {
        throw new RuntimeException(e);
    }
}

และสุดท้ายรหัสเพิ่มเติมKeyStoresSSLSocketFactoryซึ่งยอมรับ KeyStore ใหม่ของคุณและตรวจสอบว่า KeyStore ในตัวไม่สามารถตรวจสอบใบรับรอง SSL:

/**
 * Allows you to trust certificates from additional KeyStores in addition to
 * the default KeyStore
 */
public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory {
    protected SSLContext sslContext = SSLContext.getInstance("TLS");

    public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(null, null, null, null, null, null);
        sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }



    /**
     * Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
     */
    public static class AdditionalKeyStoresTrustManager implements X509TrustManager {

        protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();


        protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) {
            final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();

            try {
                // The default Trustmanager with default keystore
                final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                original.init((KeyStore) null);
                factories.add(original);

                for( KeyStore keyStore : additionalkeyStores ) {
                    final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                    additionalCerts.init(keyStore);
                    factories.add(additionalCerts);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }



            /*
             * Iterate over the returned trustmanagers, and hold on
             * to any that are X509TrustManagers
             */
            for (TrustManagerFactory tmf : factories)
                for( TrustManager tm : tmf.getTrustManagers() )
                    if (tm instanceof X509TrustManager)
                        x509TrustManagers.add( (X509TrustManager)tm );


            if( x509TrustManagers.size()==0 )
                throw new RuntimeException("Couldn't find any X509TrustManagers");

        }

        /*
         * Delegate to the default trust manager.
         */
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
            defaultX509TrustManager.checkClientTrusted(chain, authType);
        }

        /*
         * Loop over the trustmanagers until we find one that accepts our server
         */
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            for( X509TrustManager tm : x509TrustManagers ) {
                try {
                    tm.checkServerTrusted(chain,authType);
                    return;
                } catch( CertificateException e ) {
                    // ignore
                }
            }
            throw new CertificateException();
        }

        public X509Certificate[] getAcceptedIssuers() {
            final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
            for( X509TrustManager tm : x509TrustManagers )
                list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
            return list.toArray(new X509Certificate[list.size()]);
        }
    }

}

สวัสดี @emmby นี่เป็นคำตอบที่สมบูรณ์แบบสำหรับปัญหาของฉัน แต่ฉันยังไม่ได้รับการเชื่อมต่อ SSL คุณช่วยลองดูหน่อยได้ไหม? http://stackoverflow.com/questions/7822381/need-help-understanding-certificate-chains
Matthias B

ขอบคุณสำหรับการเขียนที่ยอดเยี่ยม @emmby! บางครั้งฉันก็ล่าช้าไปนานมากแล้วก็ javax.net.ssl.SSLException: อ่านข้อผิดพลาด ความคิดใด ๆ ฉันจะตั้งหมดเวลาถ้าแก้เป็นเช่นเดียวกับstackoverflow.com/questions/5909308/android-2-3-4-ssl-problem ?
Edwin Evans

3
@emmby คุณสามารถบอกได้ว่าฉันควรใส่รหัสส่งออกนี้ CLASSPATH = bcprov-jdk16-145.jar CERTSTORE = res / raw / mystore.bks ถ้า [-a $ CERTSTORE]; rm $ CERTSTORE || ทางออก 1 fi keytool \ -import \ -v \ -trustcacerts \ -alias 0 \ -file <(openssl x509 -in mycert.pem) \ -keystore $ CERTSTORE \ -storetype BKS \ -provider org.bouncycastle.jce.provider BouncyCastleProvider \ -providerpath /usr/share/java/bcprov.jar \ - เก็บรหัสผ่านบางส่วน
Rikki Tikki Tavi

1
เฮ้ @emmby ฉันใช้โซลูชันของคุณในแอปของฉันและใช้ใบรับรองที่ลงชื่อด้วยตนเองของเซิร์ฟเวอร์ของฉัน แต่ได้รับCertificateException ()ในวิธีcheckServerTrusted () ฉันพยายามแสดงความคิดเห็นว่ามีข้อผิดพลาดและทำงานได้ ถ้ามันไม่ได้ตรวจสอบใบรับรองเซิร์ฟเวอร์ของฉันฉันจะจัดการมันด้วยวิธีอื่นได้ไหมคุณช่วยแนะนำแนวทางที่ดีที่สุดในกรณีนี้ได้อย่างไร
Ankit

7
ควรทำเครื่องหมายเป็นคำตอบที่ถูกต้อง หนึ่งในคำตอบที่ละเอียดและเป็นลายลักษณ์อักษรมากที่สุดที่ฉันเคยเห็นใน SO Dope
Kachi

74

เพิ่มรหัสนี้ก่อนHttpsURLConnectionและมันจะทำ ฉันเข้าใจแล้ว.

private void trustEveryone() { 
    try { 
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ 
                    public boolean verify(String hostname, SSLSession session) { 
                            return true; 
                    }}); 
            SSLContext context = SSLContext.getInstance("TLS"); 
            context.init(null, new X509TrustManager[]{new X509TrustManager(){ 
                    public void checkClientTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public void checkServerTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public X509Certificate[] getAcceptedIssuers() { 
                            return new X509Certificate[0]; 
                    }}}, new SecureRandom()); 
            HttpsURLConnection.setDefaultSSLSocketFactory( 
                            context.getSocketFactory()); 
    } catch (Exception e) { // should never happen 
            e.printStackTrace(); 
    } 
} 

ฉันหวังว่านี่จะช่วยคุณได้


22
ดูหมายเหตุข้างต้นภายใต้คำตอบที่ยอมรับ 'การแก้ปัญหา' นี้ไม่ปลอดภัยอย่างสิ้นเชิง
มาร์ควิสแห่ง Lorne

5
นี่คือโซลูชัน Q & D ที่เหมาะสมที่สุด สั้นและ "เพิ่งได้ผล"
Steve Smith

5
คำตอบที่สมบูรณ์แบบสำหรับการทดสอบ !!! และใช่มันเป็นความคิดที่ดีที่จะใช้ในการผลิต แต่มา ... ที่ควรจะชัดเจนสำหรับทุกคนที่ดูชื่อคำถาม มันยังคงตอบได้ดีที่สุด / สั้นที่สุด / ด้วยระดับความปลอดภัยเดียวกัน (ใน)!
Levite

34

นี่เป็นความคิดที่ไม่ดี การเชื่อถือใบรับรองใด ๆ จะดีกว่าการใช้ SSL เพียงเล็กน้อย (มาก) เมื่อคุณพูดว่า "ฉันต้องการให้ลูกค้าของฉันยอมรับใบรับรองใด ๆ (เพราะฉันแค่ชี้ไปที่เซิร์ฟเวอร์เดียว)" คุณกำลังสมมติว่านี่หมายความว่าการชี้ไปที่ "เซิร์ฟเวอร์หนึ่ง" นั้นปลอดภัยซึ่งไม่ได้อยู่ในเครือข่ายสาธารณะ

คุณเปิดรับการโจมตีจากคนกลางได้อย่างสมบูรณ์โดยเชื่อถือใบรับรองใด ๆ ทุกคนสามารถเชื่อมต่อการเชื่อมต่อของคุณด้วยการสร้างการเชื่อมต่อ SSL แยกกับคุณและเซิร์ฟเวอร์ปลายทาง MITM นั้นสามารถเข้าถึงคำขอและการตอบกลับทั้งหมดของคุณ ถ้าคุณไม่ต้องการ SSL ในตอนแรก (ข้อความของคุณไม่มีความละเอียดอ่อนและไม่ทำการตรวจสอบ) คุณไม่ควรเชื่อถือใบรับรองทั้งหมดอย่างสุ่มสี่สุ่มห้า

คุณควรพิจารณาเพิ่มพับลิกใบรับรองให้กับ jks โดยใช้ keytool และใช้เพื่อสร้างโรงงานซ็อกเก็ตของคุณเช่นนี้

    KeyStore ks = KeyStore.getInstance("JKS");

    // get user password and file input stream
    char[] password = ("mykspassword")).toCharArray();
    ClassLoader cl = this.getClass().getClassLoader();
    InputStream stream = cl.getResourceAsStream("myjks.jks");
    ks.load(stream, password);
    stream.close();

    SSLContext sc = SSLContext.getInstance("TLS");
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

    kmf.init(ks, password);
    tmf.init(ks);

    sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);

    return sc.getSocketFactory();

นี่มีข้อแม้หนึ่งข้อที่ควรระวัง ใบรับรองจะหมดอายุในที่สุดและรหัสจะหยุดทำงานในเวลานั้น คุณสามารถกำหนดได้อย่างง่ายดายว่าจะเกิดอะไรขึ้นเมื่อดูที่ใบรับรอง


5
หากคุณไม่ได้ใช้การรับรองความถูกต้องใบรับรองไคลเอนต์จากฝั่งไคลเอ็นต์คุณไม่จำเป็นต้องใช้ keymanager (ใช้nullในSSLContext.init). You should also use the default algorithms (KMF/TMF.getDefaultAlgorithm() ), instead of hard-coding SunX509` (มากกว่านั้นเพราะค่าเริ่มต้นสำหรับ TMF เป็นจริงPKIXบน Sun / Oracle JVM)
Bruno

มีไฟล์รูทใบรับรองพร้อมใช้งานหรือยัง (เช่นเบราว์เซอร์ทำ)
dani herrera

Where did myjks.jksมาจากไหน
zionpi

1
@zionpi สร้างโดยใช้ Java "keytool"
ด่าน

22

คุณสามารถปิดใช้งานการตรวจสอบ HttpURLConnection SSL สำหรับการทดสอบด้วยวิธีนี้ตั้งแต่ API 8:

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    if (conn instanceof HttpsURLConnection) {
        HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
        httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
        httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
    }

2
org.apache.http.conn.ssl.AllowAllHostnameVerifierเลิกใช้แล้ว
zackygaurav

2
@zackygaurav ตามที่Javadoc , AllowAllHostnameVerifierจะถูกแทนที่ด้วยNoopHostnameVerifier"
DLight

10

API ของ HttpComponents มีการเปลี่ยนแปลง มันทำงานกับรหัสด้านล่าง

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        }, new AllowAllHostnameVerifier());

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https",8444, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        e.printStackTrace();
        return new DefaultHttpClient();
    }
}

การใช้กลยุทธ์ความน่าเชื่อถือที่กำหนดเองเป็นคำตอบที่ถูกต้อง ขอบคุณ
Matt Friedman

10

รหัสด้านบนในhttps://stackoverflow.com/a/6378872/1553004นั้นถูกต้องยกเว้นว่าจะต้องเรียกตัวตรวจสอบชื่อโฮสต์ด้วย:

    @Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
    SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    getHostnameVerifier().verify(host, sslSocket);
    return sslSocket;
}

ฉันลงทะเบียนเพื่อ stackoverflow อย่างชัดเจนเพื่อเพิ่มการแก้ไขนี้ ฟังคำเตือนของฉัน!


เมื่อคุณยืนยันใบรับรองด้วยวิธีนี้ในการเชื่อมต่อครั้งแรกคุณจะทำอย่างไรกับการเชื่อมต่อที่ตามมา? คุณใช้ความรู้ที่ได้รับจากการเชื่อมต่อครั้งแรกหรือไม่? จะเป็นอย่างไรถ้าใช้ใบรับรองปลอมที่มีชื่อเดียวกันในการพยายามเชื่อมต่อ 3
jww

6

ฉันกำลังเพิ่มการตอบกลับสำหรับผู้ที่ใช้ httpclient-4.5 และอาจใช้ได้กับ 4.4 เช่นกัน

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.fluent.ContentResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;



public class HttpClientUtils{

public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() {
    try {
        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        });
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); 
        return httpclient;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
}

NoopHostnameVerifier () คลาสใหม่คืออะไร
Mushtakim Ahmed Ansari

1
@MushtakimAhmedAnsari จากเอกสาร: "NO_OP HostnameVerifier จะปิดการตรวจสอบชื่อโฮสต์เป็นหลักการดำเนินการนี้เป็นแบบไม่ใช้งานและไม่เคยทิ้ง SSLException"
raisercostin

ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมสิ่งนี้ควรได้รับคะแนนโหวตมากขึ้น
Abhay Dwivedi

ฉันจะใช้มันได้อย่างไร หรือคุณแนะนำว่าการมีคลาสจะแทนที่การตรวจสอบใบรับรอง SSL?
behelit

ใช่. httpClient นั้นเมื่อใช้จะไม่ตรวจสอบใบรับรอง https
raisercostin

4

การเชื่อถือใบรับรองทั้งหมดไม่ใช่ทางเลือกที่แท้จริงสำหรับฉันดังนั้นฉันได้ทำสิ่งต่อไปนี้เพื่อรับ HttpsURLConnection เพื่อเชื่อถือใบรับรองใหม่ (ดูhttp://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store-) on.html )

  1. รับใบรับรอง ฉันทำสิ่งนี้เสร็จแล้วโดยส่งออกใบรับรองใน Firefox (คลิกที่ไอคอนรูปล็อคเล็ก ๆ รับรายละเอียดใบรับรองคลิกส่งออก) จากนั้นใช้พอร์ตเทลเพื่อส่งออก truststore (BKS)

  2. โหลด Truststore จาก /res/raw/geotrust_cert.bks ด้วยรหัสต่อไปนี้:

        final KeyStore trustStore = KeyStore.getInstance("BKS");
        final InputStream in = context.getResources().openRawResource(
                R.raw.geotrust_cert);
        trustStore.load(in, null);
    
        final TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
    
        final SSLContext sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(null, tmf.getTrustManagers(),
                new java.security.SecureRandom());
    
        HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx
                .getSocketFactory());

ฉันได้รับข้อผิดพลาดนี้ IOExceptionjavax.net.ssl.SSLPeerUnverifiedException: No peer certificate. สิ่งนี้เมื่อทำการเรียกใช้งานจริงบน HttpClient หลังจากการตั้งค่าข้างต้นเสร็จสิ้น
Michael

3

นี่เป็นเวอร์ชั่นที่ง่ายมากโดยใช้รหัส http.2 ของ 4.1.2 จากนั้นสามารถแก้ไขเป็นอัลกอริธึมความน่าเชื่อถือใด ๆ

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        });
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", 443, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

3

ฉันดูการตอบสนองจาก "emmby" (ตอบ 16 มิ.ย. 54 เวลา 21:29), รายการ # 4: "สร้าง SSLSocketFactory แบบกำหนดเองที่ใช้ KeyStore ในตัว แต่กลับไปที่ KeyStore สำรองสำหรับสิ่งที่ล้มเหลว เพื่อยืนยันด้วยค่าเริ่มต้น "

นี่เป็นการใช้งานที่ง่ายขึ้น โหลดที่เก็บคีย์ระบบ & ผสานด้วยแอปพลิเคชันที่เก็บคีย์

public HttpClient getNewHttpClient() {
    try {
        InputStream in = null;
        // Load default system keystore
        KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType()); 
        try {
            in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks"
            trusted.load(in, null); // no password is "changeit"
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

        // Load application keystore & merge with system
        try {
            KeyStore appTrusted = KeyStore.getInstance("BKS"); 
            in = context.getResources().openRawResource(R.raw.mykeystore);
            appTrusted.load(in, null); // no password is "changeit"
            for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) {
                final String alias = e.nextElement();
                final KeyStore.Entry entry = appTrusted.getEntry(alias, null);
                trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null);
            }
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SSLSocketFactory sf = new SSLSocketFactory(trusted);
        sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

โหมดง่าย ๆ ในการแปลงจาก JKS เป็น BKS:

keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $JAVA_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt

* หมายเหตุ: ใน Android 4.0 (ICS) Trust Store มีการเปลี่ยนแปลงข้อมูลเพิ่มเติม: http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html


3

สำหรับผู้ที่ต้องการอนุญาตให้ใบรับรองทั้งหมดทำงาน (เพื่อจุดประสงค์ในการทดสอบ) ผ่าน OAuth ให้ทำตามขั้นตอนเหล่านี้:

1) ดาวน์โหลดซอร์สโค้ดของ Android OAuth API ที่นี่: https://github.com/kaeppler/signpost

2) ค้นหาไฟล์คลาส "CommonsHttpOAuthProvider"

3) เปลี่ยนเป็นดังนี้:

public class CommonsHttpOAuthProvider extends AbstractOAuthProvider {

private static final long serialVersionUID = 1L;

private transient HttpClient httpClient;

public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
        String authorizationWebsiteUrl) {
    super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl);


    //this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA
    this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only)
}

"MySSLSocketFactory" ด้านบนเป็นไปตามคำตอบที่ยอมรับ เพื่อให้ง่ายยิ่งขึ้นนี่คือคลาสที่สมบูรณ์:

package com.netcomps.oauth_example;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

//http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
public class MySSLSocketFactory extends SSLSocketFactory {

    SSLContext sslContext = SSLContext.getInstance("TLS");

public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {

    super(truststore);
    TrustManager tm = new X509TrustManager() {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}

@Override
public Socket createSocket() throws IOException {
    return sslContext.getSocketFactory().createSocket();
}



public static HttpClient getNewHttpClient() {

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

}

หวังว่านี่จะช่วยใครซักคน


1
คำถามคือHttpClientและ HTTPS; ไม่ใช่ OAuth สำหรับ Android จากโครงการ GitHub
jww

3

ฉันใช้มันและมันใช้ได้กับฉันในทุกระบบปฏิบัติการ

/**
 * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
 * aid testing on a local box, not for use on production.
 */


private static void disableSSLCertificateChecking() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }
    } };

    try {
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

สวัสดี @ yegor256 ฉันใช้รหัสนี้ แต่ยังคงได้รับปัญหาการจับมือกับ SSL
user2028

1

เพียงแค่เพิ่ม-Dtrust_all_cert=trueข้อโต้แย้ง VM ควรทำ อาร์กิวเมนต์นี้บอกให้ java ละเว้นการตรวจสอบใบรับรอง


4
โปรดอย่าเพิ่มคำตอบเดียวกันในหลายคำถาม ตอบคำถามที่ดีที่สุดและทำเครื่องหมายส่วนที่เหลือเป็นรายการซ้ำ ดูเป็นที่ยอมรับหรือไม่ที่จะเพิ่มคำตอบซ้ำสำหรับคำถามหลายข้อ?
Petter Friberg

0

องค์กรใดก็ตามที่ยังคงประสบปัญหากับใบรับรอง StartCom SSL บน Android 2.1 โปรดไปที่https://www.startssl.com/certs/และดาวน์โหลด ca.pem ตอนนี้อยู่ในคำตอบของ@emmbyแทนที่

`export CLASSPATH=bcprov-jdk16-145.jar
 CERTSTORE=res/raw/mystore.bks
      if [ -a $CERTSTORE ]; then
          rm $CERTSTORE || exit 1
      fi
 keytool \
  -import \
  -v \
  -trustcacerts \
  -alias 0 \
  -file <(openssl x509 -in mycert.pem) \
  -keystore $CERTSTORE \
  -storetype BKS \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath /usr/share/java/bcprov.jar \
  -storepass some-password`

กับ

 `export CLASSPATH=bcprov-jdk16-145.jar
 CERTSTORE=res/raw/mystore.bks
      if [ -a $CERTSTORE ]; then
          rm $CERTSTORE || exit 1
      fi
 keytool \
  -import \
  -v \
  -trustcacerts \
  -alias 0 \
  -file <(openssl x509 -in ca.pem) \
  -keystore $CERTSTORE \
  -storetype BKS \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath /usr/share/java/bcprov.jar \
  -storepass some-password`

ควรใช้งานนอกกรอบ ฉันพยายามมานานกว่าหนึ่งวันแม้หลังจากคำตอบที่สมบูรณ์แบบโดย@emmby .. หวังว่านี่จะช่วยใครซักคน ...


0

ใช้คลาสนี้

public class WCFs
{
    //  https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";


public static Thread myMethod(Runnable rp)
{
    String METHOD_NAME = "myMethod";

    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

    request.addProperty("Message", "Https WCF Running...");
    return _call(rp,METHOD_NAME, request);
}

protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
    final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    int TimeOut = 5*1000;

    envelope.dotNet = true;
    envelope.bodyOut = soapReq;
    envelope.setOutputSoapObject(soapReq);

    final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);

    try
    {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
        {
            @Override
            public boolean verify(String hostname, SSLSession session)
            {
                return true;
            }
        });

        KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
        ((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));


    }
    catch(Exception e){}

    HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
    {
        @Override
        public void run()
        {
            Handler h = new Handler(Looper.getMainLooper());
            Object response = null;

            for(int i=0; i<4; i++)
            {
                response = send(envelope, httpTransport_net , METHOD_NAME, null);

                try
                {if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}

                if(response != null)
                    break;

                ThreadHelper.threadSleep(250);
            }

            if(response != null)
            {
                if(rp != null)
                {
                    rp.setArguments(response.toString());
                    h.post(rp);
                }
            }
            else
            {
                if(Thread.currentThread().isInterrupted())
                    return;

                if(rp != null)
                {
                    rp.setExceptionState(true);
                    h.post(rp);
                }
            }

            ThreadHelper.stopThread(this);
        }
    };

    thread.start();

    return thread;
}


private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
    try
    {
        if(headerList != null)
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
        else
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);

        Object res = envelope.getResponse();

        if(res instanceof SoapPrimitive)
            return (SoapPrimitive) envelope.getResponse();
        else if(res instanceof SoapObject)
            return ((SoapObject) envelope.getResponse());
    }
    catch(Exception e)
    {}

    return null;
}

public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
    try
    {
        InputStream inputStream = ResourceMaster.openRaw(id);
        KeyStore keystore = KeyStore.getInstance(algorithm);
        keystore.load(inputStream, filePassword.toCharArray());
        inputStream.close();

        return keystore;
    }
    catch(Exception e)
    {}

    return null;
}

public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
    try
    {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustKey);

        SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
        context.init(null, tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
    catch(Exception e){}

    return null;
}

}


0

ป้อนคำอธิบายรูปภาพที่นี่

sspi ล้มเหลวใน Android ของ xamarin

ฉันพบวิธีแก้ปัญหานี้ ใส่รหัสนี้ก่อนที่คุณจะเข้าลิงค์ HTTPS

const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;

-3

ทำงานกับ https ทั้งหมด

httpClient = new DefaultHttpClient();

SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};

ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, ssf));

2
เพียงแค่ทำซ้ำการแก้ปัญหาที่ไม่ปลอดภัยที่ผิดพลาดแบบเดียวกันซึ่งได้มีการพูดคุยและไล่ออกในหัวข้อนี้แล้ว
มาร์ควิสแห่ง Lorne

-3

มีคำตอบมากมายข้างต้น แต่ฉันไม่สามารถทำงานได้อย่างถูกต้อง (ด้วยเวลาที่ จำกัด ของฉัน) ดังนั้นสำหรับคนอื่นที่อยู่ในสถานการณ์เดียวกันคุณสามารถลองใช้โค้ดด้านล่างซึ่งทำงานได้อย่างสมบูรณ์สำหรับการทดสอบจาวาของฉัน:

    public static HttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx);
        ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        ClientConnectionManager ccm = base.getConnectionManager();
        SchemeRegistry sr = ccm.getSchemeRegistry();
        sr.register(new Scheme("https", ssf, 443));
        return new DefaultHttpClient(ccm, base.getParams());
    } catch (Exception ex) {
        return null;
    }
}

และโทรเช่น:

DefaultHttpClient baseClient = new DefaultHttpClient();
HttpClient httpClient = wrapClient(baseClient );

การอ้างอิง: http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-problem-using-apache-httpclient/


ที่จะพูด EJP: "เพียงแค่ทำซ้ำผิดพลาดที่ไม่ปลอดภัยไม่ใช่วิธีการแก้ปัญหาเช่นเดียวกับที่ได้รับการกล่าวถึงและไม่สนใจในหัวข้อนี้"
jww

-4

เพียงแค่ใช้สิ่งนี้ -

public DefaultHttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };
    ctx.init(null, new TrustManager[]{tm}, null);
    SSLSocketFactory ssf = new SSLSocketFactory(ctx);
    ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    ClientConnectionManager ccm = base.getConnectionManager();
    SchemeRegistry sr = ccm.getSchemeRegistry();
    sr.register(new Scheme("https", ssf, 443));
    return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
    return null;
}
}

ที่จะพูด EJP: "เพียงแค่ทำซ้ำผิดพลาดที่ไม่ปลอดภัยไม่ใช่วิธีการแก้ปัญหาเช่นเดียวกับที่ได้รับการกล่าวถึงและไม่สนใจในหัวข้อนี้"
jww

-5

คำตอบของ Danielนั้นดียกเว้นฉันต้องเปลี่ยนรหัสนี้ ...

    SchemeRegistry registry = new SchemeRegistry();
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", sf, 443));

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

รหัสนี้ ...

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
    SchemeRegistry registry = ccm.getShemeRegistry()
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", sf, 443));

เพื่อให้มันทำงาน


5
มันควรจะทำงานอย่างไร คุณอ้างอิงรีจิสตรีก่อนที่คุณจะสร้างมันขึ้นมา!
Matthias B
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.