1 /*
2  * Copyright 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.keychain;
18 
19 import java.io.BufferedInputStream;
20 import java.io.BufferedReader;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.PrintWriter;
25 import java.net.Socket;
26 import java.security.KeyStore;
27 
28 import javax.net.ssl.KeyManagerFactory;
29 import javax.net.ssl.SSLContext;
30 import javax.net.ssl.SSLServerSocket;
31 import javax.net.ssl.SSLServerSocketFactory;
32 
33 import android.content.Context;
34 import android.util.Base64;
35 import android.util.Log;
36 
37 public class SecureWebServer {
38 
39     // Log tag for this class
40     private static final String TAG = "SecureWebServer";
41 
42     // File name of the image used in server response
43     private static final String EMBEDDED_IMAGE_FILENAME = "training-prof.png";
44 
45     private SSLServerSocketFactory sssf;
46     private SSLServerSocket sss;
47 
48     // A flag to control whether the web server should be kept running
49     private boolean isRunning = true;
50 
51     // The base64 encoded image string used as an embedded image
52     private final String base64Image;
53 
54     /**
55      * WebServer constructor.
56      */
SecureWebServer(Context ctx)57     public SecureWebServer(Context ctx) {
58         try {
59             // Get an SSL context using the TLS protocol
60             SSLContext sslContext = SSLContext.getInstance("TLS");
61 
62             // Get a key manager factory using the default algorithm
63             KeyManagerFactory kmf = KeyManagerFactory
64                     .getInstance(KeyManagerFactory.getDefaultAlgorithm());
65 
66             // Load the PKCS12 key chain
67             KeyStore ks = KeyStore.getInstance("PKCS12");
68             FileInputStream fis = ctx.getAssets()
69                     .openFd(KeyChainDemoActivity.PKCS12_FILENAME)
70                     .createInputStream();
71             ks.load(fis, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
72             kmf.init(ks, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
73 
74             // Initialize the SSL context
75             sslContext.init(kmf.getKeyManagers(), null, null);
76 
77             // Create the SSL server socket factory
78             sssf = sslContext.getServerSocketFactory();
79 
80         } catch (Exception e) {
81             e.printStackTrace();
82         }
83 
84         // Create the base64 image string used in the server response
85         base64Image = createBase64Image(ctx);
86     }
87 
88     /**
89      * This method starts the web server listening to the port 8080
90      */
start()91     protected void start() {
92 
93         new Thread(new Runnable() {
94 
95             @Override
96             public void run() {
97                 Log.d(TAG, "Secure Web Server is starting up on port 8080");
98                 try {
99                     // Create the secure server socket
100                     sss = (SSLServerSocket) sssf.createServerSocket(8080);
101                 } catch (Exception e) {
102                     System.out.println("Error: " + e);
103                     return;
104                 }
105 
106                 Log.d(TAG, "Waiting for connection");
107                 while (isRunning) {
108                     try {
109                         // Wait for an SSL connection
110                         Socket socket = sss.accept();
111 
112                         // Got a connection
113                         Log.d(TAG, "Connected, sending data.");
114 
115                         BufferedReader in = new BufferedReader(
116                                 new InputStreamReader(socket.getInputStream()));
117                         PrintWriter out = new PrintWriter(socket
118                                 .getOutputStream());
119 
120                         // Read the data until a blank line is reached which
121                         // signifies the end of the client HTTP headers
122                         String str = ".";
123                         while (!str.equals(""))
124                             str = in.readLine();
125 
126                         // Send a HTTP response
127                         out.println("HTTP/1.0 200 OK");
128                         out.println("Content-Type: text/html");
129                         out.println("Server: Android KeyChainiDemo SSL Server");
130                         // this blank line signals the end of the headers
131                         out.println("");
132                         // Send the HTML page
133                         out.println("<H1>Welcome to Android!</H1>");
134                         // Add an embedded Android image
135                         out.println("<img src='data:image/png;base64," + base64Image + "'/>");
136                         out.flush();
137                         socket.close();
138                     } catch (Exception e) {
139                         Log.d(TAG, "Error: " + e);
140                     }
141                 }
142             }
143         }).start();
144 
145     }
146 
147     /**
148      * This method stops the SSL web server
149      */
stop()150     protected void stop() {
151         try {
152             // Break out from the infinite while loop in start()
153             isRunning = false;
154 
155             // Close the socket
156             if (sss != null) {
157                 sss.close();
158             }
159         } catch (IOException e) {
160             e.printStackTrace();
161         }
162     }
163 
164     /**
165      * This method reads a binary image from the assets folder and returns the
166      * base64 encoded image string.
167      *
168      * @param ctx The service this web server is running in.
169      * @return String The base64 encoded image string or "" if there is an
170      *         exception
171      */
createBase64Image(Context ctx)172     private String createBase64Image(Context ctx) {
173         BufferedInputStream bis;
174         try {
175             bis = new BufferedInputStream(ctx.getAssets().open(EMBEDDED_IMAGE_FILENAME));
176             byte[] embeddedImage = new byte[bis.available()];
177             bis.read(embeddedImage);
178             return Base64.encodeToString(embeddedImage, Base64.DEFAULT);
179         } catch (IOException e) {
180             e.printStackTrace();
181         }
182         return "";
183     }
184 
185 }
186