2014-07-01 9 views
5

यह एक समस्या है जो मैं हाल ही में हल करने के लिए वास्तव में संघर्ष कर रहा हूं, क्योंकि मुझे लगा कि इस मुद्दे के बारे में इंटरनेट पर लगभग बहुत अधिक जानकारी थी कि की सहायता नहीं करता था। इसलिए चूंकि मुझे अभी एक समाधान मिला है जो मेरे लिए काम करता है, मैंने फैसला किया कि मैं समस्या और समाधान यहां पोस्ट करूंगा, उम्मीद है कि मैं इंटरनेट के बाद आने वाले लोगों के लिए थोड़ा बेहतर जगह बना सकता हूं! (उम्मीद है कि यह "अनुपयोगी" सामग्री में योगदान नहीं दे रहा है!)स्व-हस्ताक्षरित प्रमाणपत्र का उपयोग कर एंड्रॉइड एसएसएलस्केट्स

मेरे पास एक एंड्रॉइड एप्लिकेशन है जिसे मैं विकसित कर रहा हूं। हाल ही में, मैं अपने ऐप और मेरे सर्वर के बीच संवाद करने के लिए सर्वरस्केट्स और सॉकेट का उपयोग कर रहा हूं। हालांकि, संचार को सुरक्षित होने की आवश्यकता है, इसलिए मैं इन्हें SSLServerSockets और SSLSockets में कनवर्ट करने की कोशिश कर रहा हूं, जो अपेक्षा की अपेक्षा से बहुत कठिन हो गया है।

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

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.InputStreamReader; 
import java.io.OutputStreamWriter; 
import java.net.InetAddress; 
import javax.net.ssl.SSLServerSocket; 
import javax.net.ssl.SSLServerSocketFactory; 
import javax.net.ssl.SSLSocket; 

/** 
* Server 
*/ 
public class simplesslserver { 
    // Global variables 
    private static SSLServerSocketFactory ssf; 
    private static SSLServerSocket ss; 
    private static final int port = 8081; 
    private static String address; 

    /** 
    * Main: Starts the server and waits for clients to connect. 
    * Each client is given its own thread. 
    * @param args 
    */ 
    public static void main(String[] args) { 
     try { 
      // System properties 
      System.setProperty("javax.net.ssl.keyStore","mykeystore.jks"); 
      System.setProperty("javax.net.ssl.keyStorePassword","MY_PASSWORD"); 

      // Start server 
      ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 
      ss = (SSLServerSocket) ssf.createServerSocket(port); 
      address = InetAddress.getLocalHost().toString(); 
      System.out.println("Server started at "+address+" on port "+port+"\n"); 

      // Wait for messages 
      while (true) { 
       SSLSocket connected = (SSLSocket) ss.accept(); 
       new clientThread(connected).start(); 
      }     
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * Client thread. 
    */ 
    private static class clientThread extends Thread { 
     // Variables 
     private SSLSocket cs; 
     private InputStreamReader isr; 
     private OutputStreamWriter osw; 
     private BufferedReader br; 
     private BufferedWriter bw; 

     /** 
     * Constructor: Initialises client socket. 
     * @param clientSocket The socket connected to the client. 
     */ 
     public clientThread(SSLSocket clientSocket) { 
      cs = clientSocket; 
     } 

     /** 
     * Starts the thread. 
     */ 
     public void run() { 
      try { 
       // Initialise streams 
       isr = new InputStreamReader(cs.getInputStream()); 
       br = new BufferedReader(isr); 
       osw = new OutputStreamWriter(cs.getOutputStream()); 
       bw = new BufferedWriter(osw); 

       // Get request from client 
       String tmp = br.readLine(); 
       System.out.println("received: "+tmp); 

       // Send response to client 
       String resp = "You said '"+tmp+"'!"; 
       bw.write(resp);   
       bw.newLine(); 
       bw.flush(); 
       System.out.println("response: "+resp); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

यह एंड्रॉयड से एक उद्धरण है:

keytool -genkey -alias serverAlias -keyalg RSA -keypass MY_PASSWORD -storepass MY_PASSWORD -keystore mykeystore.jks 

यह सर्वर कोड है:

मैं फ़ाइल "mykeystore.jks" निम्न आदेश के साथ उत्पन्न आवेदन (ग्राहक):

String message = "Hello World"; 
try{ 
    // Create SSLSocketFactory 
    SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); 

    // Create socket using SSLSocketFactory 
    SSLSocket client = (SSLSocket) factory.createSocket("SERVER_IP_ADDRESS", 8081); 

    // Print system information 
    System.out.println("Connected to server " + client.getInetAddress() + ": " + client.getPort()); 

    // Writer and Reader 
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); 
    BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); 

    // Send request to server 
    System.out.println("Sending request: "+message); 
    writer.write(message); 
    writer.newLine(); 
    writer.flush(); 

    // Receive response from server 
    String response = reader.readLine(); 
    System.out.println("Received from the Server: "+response); 

    // Close connection 
    client.close(); 

    return response; 
} catch(Exception e) { 
    e.printStackTrace(); 
} 
return "Something went wrong..."; 

जब मैं कोड चलाता था, यह काम नहीं करता था, और यह वह आउटपुट है जो मुझे मिल रहा था।

ग्राहक उत्पादन:

Connected to server /SERVER_IP_ADDRESS: 8081 
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 
... stack trace ... 

सर्वर उत्पादन:

received: null 
java.net.SocketException: Connection closed by remote host 

प्रमाण पत्र के रूप में स्व-हस्ताक्षरित है, एप्लिकेशन यह भरोसा नहीं करता है। मैंने Google के चारों ओर एक नज़र डाली, और सामान्य सहमति यह है कि मुझे एक एसएसएलकॉन्टेक्स्ट (क्लाइंट में) बनाने की ज़रूरत है जो कस्टम ट्रस्टमैनेजर पर आधारित है जो इस स्व-हस्ताक्षरित प्रमाणपत्र को स्वीकार करता है। काफी सरल, मैंने सोचा। अगले हफ्ते में, मैंने इस मुद्दे को हल करने के अधिक तरीकों की कोशिश की, जिसे मैं संभवतः याद कर सकता हूं, इसका कोई फायदा नहीं हुआ। अब मैं आपको अपने मूल कथन पर वापस भेजता हूं: वहां बहुत अधिक अपूर्ण जानकारी है, जिसने समाधान को समझने के लिए बहुत कठिन परिश्रम किया है।

मुझे मिला एकमात्र कामकाजी समाधान, एक ट्रस्टमैनेजर बनाना था जो सभी प्रमाण पत्र स्वीकार करता है।

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

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

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

जो इस

SSLContext sslctx = SSLContext.getInstance("TLS"); 
sslctx.init(new KeyManager[0], new TrustManager[] {new AcceptAllTrustManager()}, new SecureRandom()); 
SSLSocketFactory factory = sslctx.getSocketFactory(); 
SSLSocket client = (SSLSocket) factory.createSocket("IP_ADDRESS", 8081); 

की तरह इस्तेमाल किया जा सकता और बिना किसी अपवाद के अच्छा और खुश उत्पादन देता है!

Connected to server /SERVER_IP_ADDRESS: 8081 
Sending request: Hello World 
Received from the Server: You said 'Hello World'! 

हालांकि, यह नहीं एक अच्छा विचार, क्योंकि एप्लिकेशन को अभी भी असुरक्षित हो जाएगा, संभावित मैन-इन-द-मिडल हमलों के लिए धन्यवाद है।

तो मैं अटक गया था। मैं ऐप को अपने स्वयं के हस्ताक्षरित प्रमाण पत्र पर भरोसा कैसे प्राप्त कर सकता हूं, लेकिन वहां कोई भी प्रमाणपत्र नहीं है?

उत्तर

3

मुझे एक ऐसा तरीका मिला जो स्पष्ट रूप से एक SSLSontext के आधार पर एक SSLSocket बनाना चाहिए जो ट्रस्टमैनेजर पर आधारित है जो mykeystore पर भरोसा करता है। चाल है, हमें कस्टम ट्रस्ट मैनेजर में कीस्टोर लोड करने की आवश्यकता है, जैसे कि SSLSocket SSLContext पर आधारित है जो मेरे स्वयं के हस्ताक्षरित प्रमाणपत्र पर भरोसा करती है। यह ट्रस्ट मैनेजर में कीस्टोर लोड करके किया जाता है।

कोड मैं इस प्रकार थी क्या करने के लिए मिल गया:

KeyStore keyStore = KeyStore.getInstance("JKS"); 
keyStore.load(getResources().openRawResource(R.raw.mykeystore), "MY_PASSWORD".toCharArray()); 

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
trustManagerFactory.init(keyStore); 

SSLContext sslctx = SSLContext.getInstance("TLS"); 
sslctx.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); 

SSLSocketFactory factory = sslctx.getSocketFactory(); 

कौन सा तुरंत विफल रहा है।

java.security.KeyStoreException: java.security.NoSuchAlgorithmException: KeyStore JKS implementation not found 

जाहिर है, एंड्रॉइड जेकेएस का समर्थन नहीं करता है। यह बीकेएस प्रारूप में होना चाहिए।

तो मैं निम्न आदेश चलाकर JKS से बीकेएस कन्वर्ट करने के लिए एक रास्ता मिल गया:

keytool -importkeystore -srckeystore mykeystore.jks -destkeystore mykeystore.bks -srcstoretype JKS -deststoretype BKS -srcstorepass MY_PASSWORD -deststorepass MY_PASSWORD -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk15on-150.jar 

अब, मैं एक फ़ाइल mykeystore.bks कहा जाता है जो वास्तव में mykeystore.jks रूप में ही है, को छोड़कर बीकेएस प्रारूप (जो एकमात्र प्रारूप एंड्रॉइड स्वीकार करता है)।

मेरे एंड्रॉइड ऐप के साथ "mykeystore.bks" और मेरे सर्वर के साथ "mykeystore.jks" का उपयोग करके, यह काम करता है!

ग्राहक उत्पादन:

Connected to server /SERVER_IP_ADDRESS: 8081 
Sending request: Hello World 
Received from the Server: You said 'Hello World'! 

सर्वर उत्पादन:

received: Hello World 
response: You said 'Hello World'! 

हो गया! मेरे एंड्रॉइड एप्लिकेशन और मेरे सर्वर के बीच SSLServerSocket/SSLSocket कनेक्शन अब मेरे स्व-हस्ताक्षरित प्रमाणपत्र के साथ काम कर रहा है।

यहाँ मेरी Android एप्लिकेशन में अंतिम कोड है:

String message = "Hello World"; 
try{ 
    // Load the server keystore 
    KeyStore keyStore = KeyStore.getInstance("BKS"); 
    keyStore.load(ctx.getResources().openRawResource(R.raw.mykeystore), "MY_PASSWORD".toCharArray()); 

    // Create a custom trust manager that accepts the server self-signed certificate 
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    trustManagerFactory.init(keyStore); 

    // Create the SSLContext for the SSLSocket to use 
    SSLContext sslctx = SSLContext.getInstance("TLS"); 
    sslctx.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); 

    // Create SSLSocketFactory 
    SSLSocketFactory factory = sslctx.getSocketFactory(); 

    // Create socket using SSLSocketFactory 
    SSLSocket client = (SSLSocket) factory.createSocket("SERVER_IP_ADDRESS", 8081); 

    // Print system information 
    System.out.println("Connected to server " + client.getInetAddress() + ": " + client.getPort()); 

    // Writer and Reader 
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); 
    BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); 

    // Send request to server 
    System.out.println("Sending request: "+message); 
    writer.write(message); 
    writer.newLine(); 
    writer.flush(); 

    // Receive response from server 
    String response = reader.readLine(); 
    System.out.println("Received from the Server: "+response); 

    // Close connection 
    client.close(); 

    return response; 
} catch(Exception e) { 
    e.printStackTrace(); 
} 
return "Something went wrong..."; 

(ध्यान दें कि सर्वर कोड नहीं बदला है, और पूर्ण सर्वर कोड मूल प्रश्न में है।)

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