2014-12-19 15 views
9

रखते हुए प्रोग्रामेटिक रूप से प्रमाण पत्र प्राधिकरण जोड़ें StackOverflow पर इस विषय के बारे में बहुत सारे प्रश्न हैं, लेकिन मुझे मेरी समस्या से संबंधित कोई नहीं लगता है।एंड्रॉइड सिस्टम एसएसएल प्रमाणपत्र

मेरे पास एक एंड्रॉइड एप्लिकेशन है जिसे HTTPS सर्वर के साथ संवाद करने की आवश्यकता है: कुछ एंड्रॉइड सिस्टम कीस्टोर (सामान्य HTTPS वेबसाइट) में पंजीकृत सीए के साथ हस्ताक्षर किए गए हैं, और कुछ ने सीए के साथ हस्ताक्षर किए हैं लेकिन एंड्रॉइड सिस्टम कीस्टोर में नहीं हैं (उदाहरण के लिए एक स्वत: हस्ताक्षरित प्रमाणपत्र वाला एक सर्वर)।

मुझे पता है कि मेरे सीए प्रोग्रामेटिक रूप से कैसे जोड़ना है और इसका उपयोग करने के लिए प्रत्येक HTTPS कनेक्शन को मजबूर करना है। मैं निम्नलिखित कोड का उपयोग करें:

public class SslCertificateAuthority { 

    public static void addCertificateAuthority(InputStream inputStream) { 

     try { 
      // Load CAs from an InputStream 
      // (could be from a resource or ByteArrayInputStream or ...) 
      CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
      InputStream caInput = new BufferedInputStream(inputStream); 
      Certificate ca; 
      try { 
       ca = cf.generateCertificate(caInput); 
      } finally { 
       caInput.close(); 
      } 

      // Create a KeyStore containing our trusted CAs 
      String keyStoreType = KeyStore.getDefaultType(); 
      KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
      keyStore.load(null, null); 
      keyStore.setCertificateEntry("ca", ca); 

      // Create a TrustManager that trusts the CAs in our KeyStore 
      String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
      tmf.init(keyStore); 

      // Create an SSLContext that uses our TrustManager 
      SSLContext context = SSLContext.getInstance("TLS"); 
      context.init(null, tmf.getTrustManagers(), null); 

      // Tell the URLConnection to use a SocketFactory from our SSLContext 
      HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); 
     } catch (CertificateException e) { 
      e.printStackTrace(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (KeyStoreException e) { 
      e.printStackTrace(); 
     } catch (KeyManagementException e) { 
      e.printStackTrace(); 
     } catch (MalformedURLException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 

} 

हालांकि, कर कि एंड्रॉयड प्रणाली कुंजीस्टोर के उपयोग को अक्षम कर देता है, और मैं HTTPS क्वेरी नहीं कर सकता साइटों अन्य सीए किसी भी अधिक के साथ हस्ताक्षर किए। (एक अपवाद शुरू की है)

KeyStore.getInstance("AndroidCAStore") 

... लेकिन मैं तो यह मेरे सीए नहीं जोड़ सकते हैं:

मैं एंड्रॉयड कुंजीस्टोर में मेरी CA जोड़ने की कोशिश की, का उपयोग करते हुए।

मैं अपने सीए का उपयोग करने के मामले के मामले में मामले के आधार पर स्थिर वैश्विक HttpsURLConnection.setDefaultSSLSocketFactory(...) के बजाय उदाहरण विधि HttpsURLConnection.setSSLSocketFactory(...) का उपयोग कर सकता हूं।

लेकिन यह व्यावहारिक नहीं है, क्योंकि कभी-कभी मैं कुछ पुस्तकालयों में पूर्व-कॉन्फ़िगर किए गए HttpsURLConnection ऑब्जेक्ट को पास नहीं कर सकता।

कुछ विचार मैं कैसे कर सकता हूं?


संपादित करें - उत्तर

ठीक है, यह देखते हुए सलाह के बाद, यहाँ मेरे कार्य कोड है। इसे कुछ संवर्द्धन की आवश्यकता हो सकती है, लेकिन ऐसा लगता है कि यह एक शुरुआती बिंदु के रूप में काम करता है।

public class SslCertificateAuthority { 

    private static class UnifiedTrustManager implements X509TrustManager { 
     private X509TrustManager defaultTrustManager; 
     private X509TrustManager localTrustManager; 
     public UnifiedTrustManager(KeyStore localKeyStore) throws KeyStoreException { 
      try { 
       this.defaultTrustManager = createTrustManager(null); 
       this.localTrustManager = createTrustManager(localKeyStore); 
      } catch (NoSuchAlgorithmException e) { 
       e.printStackTrace(); 
      } 
     } 
     private X509TrustManager createTrustManager(KeyStore store) throws NoSuchAlgorithmException, KeyStoreException { 
      String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
      tmf.init((KeyStore) store); 
      TrustManager[] trustManagers = tmf.getTrustManagers(); 
      return (X509TrustManager) trustManagers[0]; 
     } 
     public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
      try { 
       defaultTrustManager.checkServerTrusted(chain, authType); 
      } catch (CertificateException ce) { 
       localTrustManager.checkServerTrusted(chain, authType); 
      } 
     } 
     @Override 
     public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
      try { 
       defaultTrustManager.checkClientTrusted(chain, authType); 
      } catch (CertificateException ce) { 
       localTrustManager.checkClientTrusted(chain, authType); 
      } 
     } 
     @Override 
     public X509Certificate[] getAcceptedIssuers() { 
      X509Certificate[] first = defaultTrustManager.getAcceptedIssuers(); 
      X509Certificate[] second = localTrustManager.getAcceptedIssuers(); 
      X509Certificate[] result = Arrays.copyOf(first, first.length + second.length); 
      System.arraycopy(second, 0, result, first.length, second.length); 
      return result; 
     } 
    } 

    public static void setCustomCertificateAuthority(InputStream inputStream) { 

     try { 
      // Load CAs from an InputStream 
      // (could be from a resource or ByteArrayInputStream or ...) 
      CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
      InputStream caInput = new BufferedInputStream(inputStream); 
      Certificate ca; 
      try { 
       ca = cf.generateCertificate(caInput); 
       System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); 
      } finally { 
       caInput.close(); 
      } 

      // Create a KeyStore containing our trusted CAs 
      String keyStoreType = KeyStore.getDefaultType(); 
      KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
      keyStore.load(null, null); 
      keyStore.setCertificateEntry("ca", ca); 

      // Create a TrustManager that trusts the CAs in our KeyStore and system CA 
      UnifiedTrustManager trustManager = new UnifiedTrustManager(keyStore); 

      // Create an SSLContext that uses our TrustManager 
      SSLContext context = SSLContext.getInstance("TLS"); 
      context.init(null, new TrustManager[]{trustManager}, null); 

      // Tell the URLConnection to use a SocketFactory from our SSLContext 
      HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); 

     } catch (CertificateException e) { 
      e.printStackTrace(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (KeyStoreException e) { 
      e.printStackTrace(); 
     } catch (KeyManagementException e) { 
      e.printStackTrace(); 
     } catch (MalformedURLException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 
+0

क्या आप सुनिश्चित हैं कि आपका कोड दोनों विश्वसनीय प्रमाण और स्वयं हस्ताक्षरित दोनों के साथ काम कर रहा है? मैंने कोशिश की है, यह केवल इनपुट स्ट्रीम से उस प्रमाणपत्र के साथ काम करता है; जब मैं विश्वसनीय प्रमाणपत्र के साथ सर्वर पर स्विच करता हूं, विधि "चेकसेवर ट्रस्टेड" अपवाद बढ़ता है –

+0

क्या आपका परीक्षण एंड्रॉइड एन या पिछले संस्करण के साथ किया गया था? –

+0

मैंने 4.4, 6.0 –

उत्तर

3

कोशिश एक कस्टम विश्वास प्रबंधक लागू करने के लिए, इतना है कि यह अपने कस्टम प्रमाण पत्र की जांच करता है और अगर यह एंड्रॉयड builtin प्रमाण पत्र विफल रहता है।

इस आलेख को देखें: Using a Custom Certificate Trust Store on Android

मुझे लगता है कि पैराग्राफ "एक गतिशील ट्रस्टमैनेजर बनाना" ठीक से संभालता है जो आप पूछ रहे हैं।

-1

यह बहुत देर हो सकती है लेकिन यह एक कोशिश की गई और परीक्षण की गई दृष्टिकोण है जो जावा द्वारा किए गए प्रमाणपत्र चेक को बाईपास करने में मदद करती है।

मैं इस कोड के लिए क्रेडिट का दावा नहीं कर सकता, यह मेरे सहयोगियों में से एक द्वारा लिखा गया था :)। इसका उपयोग आपके कोड का परीक्षण करने के लिए विकास के दौरान किया जा सकता है। यदि आप प्रमाण पत्र से निपटना नहीं चाहते हैं, तो आप जावा को हमेशा अपने HttpURLConnection ऑब्जेक्ट के लिए किसी होस्ट से प्रमाण पत्र बना सकते हैं। ऐसा लगता है कि आप यहां क्या करने की कोशिश कर रहे हैं।

import javax.net.ssl.*; 
import java.net.HttpURLConnection; 
import java.security.KeyManagementException; 
import java.security.NoSuchAlgorithmException; 
import java.security.cert.CertificateException; 
import java.security.cert.X509Certificate; 

/*** 
* Should only be used in development, this class will allow connections to an HTTPS server with unverified certificates. 
* obviously this should not be used in the real world 
*/ 
public class TrustModifier { 
private static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); 
private static SSLSocketFactory factory; 

/** 
* Call this with any HttpURLConnection, and it will modify the trust settings if it is an HTTPS connection. 
* 
* @param conn the {@link HttpURLConnection} instance 
* @throws KeyManagementException if an error occurs while initializing the context object for the TLS protocol 
* @throws NoSuchAlgorithmException if no Provider supports a TrustManagerFactorySpi implementation for the TLS protocol. 
*/ 
public static void relaxHostChecking(HttpURLConnection conn) throws KeyManagementException, NoSuchAlgorithmException { 
    if (conn instanceof HttpsURLConnection) { 
     HttpsURLConnection httpsConnection = (HttpsURLConnection) conn; 
     SSLSocketFactory factory = prepFactory(); 
     httpsConnection.setSSLSocketFactory(factory); 
     httpsConnection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); 
    } 
} 

/** 
* Returns an {@link SSLSocketFactory} instance for the protocol being passed, this represents a secure communication context 
* 
* @return a {@link SSLSocketFactory} object for the TLS protocol 
* @throws NoSuchAlgorithmException if no Provider supports a TrustManagerFactorySpi implementation for the specified protocol. 
* @throws KeyManagementException if an error occurs while initializing the context object 
*/ 
static synchronized SSLSocketFactory prepFactory() throws NoSuchAlgorithmException, KeyManagementException { 
    if (factory == null) { 
     SSLContext ctx = SSLContext.getInstance("TLS"); 
     ctx.init(null, new TrustManager[]{new AlwaysTrustManager()}, null); 
     factory = ctx.getSocketFactory(); 
    } 
    return factory; 
} 

private static final class TrustingHostnameVerifier implements HostnameVerifier { 
    public boolean verify(String hostname, SSLSession session) { 
     return true; 
    } 
} 

private static class AlwaysTrustManager implements X509TrustManager { 
    public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { 
    } 

    public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { 
    } 

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

तुम सब करने की ज़रूरत है फोन समारोह relaxHostChecking() इस तरह है::

यहां कक्षा की मदद करनी चाहिए कि आप करते हैं कि

if (conn instanceof HttpsURLConnection) { 
     TrustModifier.relaxHostChecking(conn); 
    } 

यह जावा पर भरोसा करने में परिणाम होगा जो भी मेजबान आप HttpURLConnection का उपयोग करने के लिए कनेक्ट करने का प्रयास कर रहे हैं।

+2

बुरा विचार। बिंदु * अक्षम * सुरक्षा नहीं है बल्कि एक बार * कस्टम * और * सिस्टम * प्राधिकरणों का उपयोग करने के लिए है। –

+0

@ChrisStratton मेरे कोड का बिंदु सुरक्षा को अक्षम करना था। यह पूरी तरह से विकासात्मक उद्देश्यों के लिए है यदि कोई व्यक्ति उस मशीन के लिए एक कीस्टोर में लगातार प्रमाणपत्र नहीं डालना चाहता जो वे उपयोग करने का प्रयास करते हैं। –

+1

फिर आप प्रश्न को पढ़ने में असफल रहे - यहां लक्ष्य ** अक्षम नहीं है ** सुरक्षा अक्षम करने के लिए, इसलिए आपका प्रस्ताव सहायक नहीं है, विशेष रूप से यह मानते हुए कि * वास्तविक * लक्ष्य बहुत पहले हासिल किया गया था। –

संबंधित मुद्दे