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
17package com.example.android.keychain;
18
19import java.io.BufferedInputStream;
20import java.io.BufferedReader;
21import java.io.FileInputStream;
22import java.io.IOException;
23import java.io.InputStreamReader;
24import java.io.PrintWriter;
25import java.net.Socket;
26import java.security.KeyStore;
27
28import javax.net.ssl.KeyManagerFactory;
29import javax.net.ssl.SSLContext;
30import javax.net.ssl.SSLServerSocket;
31import javax.net.ssl.SSLServerSocketFactory;
32
33import android.content.Context;
34import android.util.Base64;
35import android.util.Log;
36
37public 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     */
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     */
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     */
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     */
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