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