am 1a2ddb42: sdk doc change: Added KeyChain API Demo
* commit '1a2ddb4216d81f9feadfea3a94bc9923255fe49a': sdk doc change: Added KeyChain API Demo
This commit is contained in:
@@ -170,6 +170,7 @@ development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLive
|
|||||||
development/samples/Home samples/${PLATFORM_NAME}/Home
|
development/samples/Home samples/${PLATFORM_NAME}/Home
|
||||||
development/samples/HoneycombGallery samples/${PLATFORM_NAME}/HoneycombGallery
|
development/samples/HoneycombGallery samples/${PLATFORM_NAME}/HoneycombGallery
|
||||||
development/samples/JetBoy samples/${PLATFORM_NAME}/JetBoy
|
development/samples/JetBoy samples/${PLATFORM_NAME}/JetBoy
|
||||||
|
development/samples/KeyChainDemo samples/${PLATFORM_NAME}/KeyChainDemo
|
||||||
development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
|
development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
|
||||||
development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution
|
development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution
|
||||||
development/samples/NotePad samples/${PLATFORM_NAME}/NotePad
|
development/samples/NotePad samples/${PLATFORM_NAME}/NotePad
|
||||||
|
|||||||
16
samples/KeyChainDemo/Android.mk
Normal file
16
samples/KeyChainDemo/Android.mk
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := samples
|
||||||
|
|
||||||
|
# Only compile source java files in this apk.
|
||||||
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
|
||||||
|
LOCAL_PACKAGE_NAME := KeyChainDemo
|
||||||
|
|
||||||
|
LOCAL_SDK_VERSION := current
|
||||||
|
|
||||||
|
include $(BUILD_PACKAGE)
|
||||||
|
|
||||||
|
# Use the following include to make our test apk.
|
||||||
|
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||||
44
samples/KeyChainDemo/AndroidManifest.xml
Normal file
44
samples/KeyChainDemo/AndroidManifest.xml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.android.keychain"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="15" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name" >
|
||||||
|
<activity
|
||||||
|
android:name="com.example.android.keychain.KeyChainDemoActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:screenOrientation="portrait" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<service android:name="com.example.android.keychain.SecureWebServerService" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
50
samples/KeyChainDemo/_index.html
Normal file
50
samples/KeyChainDemo/_index.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<p>This is a demo application highlighting how to use the KeyChain APIs introduced in API Level 14.</p>
|
||||||
|
|
||||||
|
<p>The source code for this demo app shows how to install a PKCS12 key chain stored in the assets folder,
|
||||||
|
grant permission to the app to use the installed key chain and finally display the certificate and
|
||||||
|
private key info. The app also has a simple implementation of a web server listening to requests
|
||||||
|
at an SSL socket using the same certificate in the key chain.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>The application includes the following key classes:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="src/com/example/android/keychain/KeyChainDemoActivity.html"><code>KeyChainDemoActivity</code></a>
|
||||||
|
— the main <code>Activity</code> that is used to install the key chain and start/stop
|
||||||
|
the web server. </li>
|
||||||
|
<li><a href="src/com/example/android/keychain/SecureWebServer.html"><code>SecureWebServer</code></a>
|
||||||
|
— a single thread web server listening at port 8080 for <code>https://localhost:8080</code> request</li>
|
||||||
|
<li><a href="src/com/example/android/keychain/SecureWebServerService.html"><code>SecureWebServerService</code></a>
|
||||||
|
— a <code>Service</code> that runs the web server in the foreground.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>If you are developing an application that uses the KeyChain APIs,
|
||||||
|
remember that the feature is supported only on Android 4.0 (API level 14) and
|
||||||
|
higher versions of the platform. To ensure that your application can only be
|
||||||
|
installed on devices that are running Android 4.0, remember to add the
|
||||||
|
following to the application's manifest:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code><uses-sdk android:minSdkVersion="14" /></code>, which
|
||||||
|
indicates to the Android platform that your application requires
|
||||||
|
Android 4.0 or higher. For more information, see <a
|
||||||
|
href="../../../guide/appendix/api-levels.html">API Levels</a> and the
|
||||||
|
documentation for the <a
|
||||||
|
href="../../../guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
|
||||||
|
element.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>Note: Due to browser cache, you need to restart the browser completely before it can recognize
|
||||||
|
any keystore updates. The easiest way to do this is to dismiss the app in the Recent Apps list.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For more information about using the KeyChain API, see the
|
||||||
|
<a href="../../../reference/android/security/KeyChain.html">
|
||||||
|
<code>android.security.KeyChain</code></a>
|
||||||
|
documentation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<img alt="" src="../images/KeyChainDemo1.png" />
|
||||||
|
<img alt="" src="../images/KeyChainDemo2.png" />
|
||||||
|
<img alt="" src="../images/KeyChainDemo3.png" />
|
||||||
|
<img alt="" src="../images/KeyChainDemo4.png" />
|
||||||
|
|
||||||
BIN
samples/KeyChainDemo/assets/keychain.p12
Normal file
BIN
samples/KeyChainDemo/assets/keychain.p12
Normal file
Binary file not shown.
BIN
samples/KeyChainDemo/assets/training-prof.png
Normal file
BIN
samples/KeyChainDemo/assets/training-prof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
BIN
samples/KeyChainDemo/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
samples/KeyChainDemo/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
samples/KeyChainDemo/res/drawable-ldpi/ic_launcher.png
Normal file
BIN
samples/KeyChainDemo/res/drawable-ldpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
samples/KeyChainDemo/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
samples/KeyChainDemo/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
93
samples/KeyChainDemo/res/layout/main.xml
Normal file
93
samples/KeyChainDemo/res/layout/main.xml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/keychain_label" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/keychain_button"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/keychain_install" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/server_label" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/server_button"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/server_start" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/test_ssl_button"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/test_ssl" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/cert_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cert"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:gravity="left" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="@string/private_key_label" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/private_key"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:gravity="left" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
33
samples/KeyChainDemo/res/values/strings.xml
Normal file
33
samples/KeyChainDemo/res/values/strings.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">KeyChainDemo</string>
|
||||||
|
<string name="cert_label">Certificate</string>
|
||||||
|
<string name="private_key_label">Private Key</string>
|
||||||
|
<string name="keychain_label">Key Chain Installation</string>
|
||||||
|
<string name="keychain_install">Install</string>
|
||||||
|
<string name="keychain_installed">Installed</string>
|
||||||
|
<string name="server_label">Simple SSL Web Server</string>
|
||||||
|
<string name="server_start">Start</string>
|
||||||
|
<string name="server_stop">Stop</string>
|
||||||
|
<string name="test_ssl">Test</string>
|
||||||
|
<string name="ticker_text">Starting KeyChainDemo SSL Server</string>
|
||||||
|
<string name="notification_title">KeyChainDemo SSL Server</string>
|
||||||
|
<string name="notification_message">Click to stop the server</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.keychain;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChainAliasCallback;
|
||||||
|
import android.security.KeyChainException;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
public class KeyChainDemoActivity extends Activity implements
|
||||||
|
KeyChainAliasCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file name of the PKCS12 file used
|
||||||
|
*/
|
||||||
|
public static final String PKCS12_FILENAME = "keychain.p12";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pass phrase of the PKCS12 file
|
||||||
|
*/
|
||||||
|
public static final String PKCS12_PASSWORD = "changeit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intent extra name to indicate to stop server
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_STOP_SERVER = "stop_server";
|
||||||
|
|
||||||
|
// Log tag for this class
|
||||||
|
private static final String TAG = "KeyChainApiActivity";
|
||||||
|
|
||||||
|
// Alias for certificate
|
||||||
|
private static final String DEFAULT_ALIAS = "My Key Chain";
|
||||||
|
|
||||||
|
// Name of the application preference
|
||||||
|
private static final String KEYCHAIN_PREF = "keychain";
|
||||||
|
|
||||||
|
// Name of preference name that saves the alias
|
||||||
|
private static final String KEYCHAIN_PREF_ALIAS = "alias";
|
||||||
|
|
||||||
|
// Request code used when starting the activity using the KeyChain install
|
||||||
|
// intent
|
||||||
|
private static final int INSTALL_KEYCHAIN_CODE = 1;
|
||||||
|
|
||||||
|
// Test SSL URL
|
||||||
|
private static final String TEST_SSL_URL = "https://localhost:8080";
|
||||||
|
|
||||||
|
// Button to start/stop the simple SSL web server
|
||||||
|
private Button serverButton;
|
||||||
|
|
||||||
|
// Button to install the key chain
|
||||||
|
private Button keyChainButton;
|
||||||
|
|
||||||
|
// Button to launch the browser for testing https://localhost:8080
|
||||||
|
private Button testSslButton;
|
||||||
|
|
||||||
|
/** Called when the activity is first created. */
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Set the view using the main.xml layout
|
||||||
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
|
// Check whether the key chain is installed or not. This takes time and
|
||||||
|
// should be done in another thread other than the main thread.
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isKeyChainAccessible()) {
|
||||||
|
// Key chain installed. Disable the install button and print
|
||||||
|
// the key chain information
|
||||||
|
disableKeyChainButton();
|
||||||
|
printInfo();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Key Chain is not accessible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// Setup the key chain installation button
|
||||||
|
keyChainButton = (Button) findViewById(R.id.keychain_button);
|
||||||
|
keyChainButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
installPkcs12();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setup the simple SSL web server start/stop button
|
||||||
|
serverButton = (Button) findViewById(R.id.server_button);
|
||||||
|
serverButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (serverButton.getText().equals(
|
||||||
|
getResources().getString(R.string.server_start))) {
|
||||||
|
serverButton.setText(R.string.server_stop);
|
||||||
|
startServer();
|
||||||
|
} else {
|
||||||
|
serverButton.setText(R.string.server_start);
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setup the test SSL page button
|
||||||
|
testSslButton = (Button) findViewById(R.id.test_ssl_button);
|
||||||
|
testSslButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent i = new Intent(Intent.ACTION_VIEW, Uri
|
||||||
|
.parse(TEST_SSL_URL));
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will be called when the user click on the notification to stop the
|
||||||
|
* SSL server
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
Log.d(TAG, "In onNewIntent()");
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
boolean isStopServer = intent.getBooleanExtra(EXTRA_STOP_SERVER, false);
|
||||||
|
if (isStopServer) {
|
||||||
|
serverButton.setText(R.string.server_start);
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements the KeyChainAliasCallback
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void alias(String alias) {
|
||||||
|
if (alias != null) {
|
||||||
|
setAlias(alias); // Set the alias in the application preference
|
||||||
|
disableKeyChainButton();
|
||||||
|
printInfo();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "User hit Disallow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the alias of the key chain from the application
|
||||||
|
* preference
|
||||||
|
*
|
||||||
|
* @return The alias of the key chain
|
||||||
|
*/
|
||||||
|
private String getAlias() {
|
||||||
|
SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
|
||||||
|
MODE_PRIVATE);
|
||||||
|
return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the alias of the key chain to the application preference
|
||||||
|
*/
|
||||||
|
private void setAlias(String alias) {
|
||||||
|
SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
|
||||||
|
MODE_PRIVATE);
|
||||||
|
Editor editor = pref.edit();
|
||||||
|
editor.putString(KEYCHAIN_PREF_ALIAS, alias);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method prints the key chain information.
|
||||||
|
*/
|
||||||
|
private void printInfo() {
|
||||||
|
String alias = getAlias();
|
||||||
|
X509Certificate[] certs = getCertificateChain(alias);
|
||||||
|
final PrivateKey privateKey = getPrivateKey(alias);
|
||||||
|
final StringBuffer sb = new StringBuffer();
|
||||||
|
for (X509Certificate cert : certs) {
|
||||||
|
sb.append(cert.getIssuerDN());
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
TextView certTv = (TextView) findViewById(R.id.cert);
|
||||||
|
TextView privateKeyTv = (TextView) findViewById(R.id.private_key);
|
||||||
|
certTv.setText(sb.toString());
|
||||||
|
privateKeyTv.setText(privateKey.getFormat() + ":" + privateKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will launch an intent to install the key chain
|
||||||
|
*/
|
||||||
|
private void installPkcs12() {
|
||||||
|
try {
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(getAssets().open(
|
||||||
|
PKCS12_FILENAME));
|
||||||
|
byte[] keychain = new byte[bis.available()];
|
||||||
|
bis.read(keychain);
|
||||||
|
|
||||||
|
Intent installIntent = KeyChain.createInstallIntent();
|
||||||
|
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychain);
|
||||||
|
installIntent.putExtra(KeyChain.EXTRA_NAME, DEFAULT_ALIAS);
|
||||||
|
startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == INSTALL_KEYCHAIN_CODE) {
|
||||||
|
switch (resultCode) {
|
||||||
|
case Activity.RESULT_OK:
|
||||||
|
chooseCert();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chooseCert() {
|
||||||
|
KeyChain.choosePrivateKeyAlias(this, this, // Callback
|
||||||
|
new String[] {}, // Any key types.
|
||||||
|
null, // Any issuers.
|
||||||
|
"localhost", // Any host
|
||||||
|
-1, // Any port
|
||||||
|
DEFAULT_ALIAS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate[] getCertificateChain(String alias) {
|
||||||
|
try {
|
||||||
|
return KeyChain.getCertificateChain(this, alias);
|
||||||
|
} catch (KeyChainException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateKey getPrivateKey(String alias) {
|
||||||
|
try {
|
||||||
|
return KeyChain.getPrivateKey(this, alias);
|
||||||
|
} catch (KeyChainException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks if the key chain is installed
|
||||||
|
*
|
||||||
|
* @return true if the key chain is not installed or allowed
|
||||||
|
*/
|
||||||
|
private boolean isKeyChainAccessible() {
|
||||||
|
return getCertificateChain(getAlias()) != null
|
||||||
|
&& getPrivateKey(getAlias()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method starts the background service of the simple SSL web server
|
||||||
|
*/
|
||||||
|
private void startServer() {
|
||||||
|
Intent secureWebServerIntent = new Intent(this,
|
||||||
|
SecureWebServerService.class);
|
||||||
|
startService(secureWebServerIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method stops the background service of the simple SSL web server
|
||||||
|
*/
|
||||||
|
private void stopServer() {
|
||||||
|
Intent secureWebServerIntent = new Intent(this,
|
||||||
|
SecureWebServerService.class);
|
||||||
|
stopService(secureWebServerIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenient method to disable the key chain install button
|
||||||
|
*/
|
||||||
|
private void disableKeyChainButton() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
keyChainButton.setText(R.string.keychain_installed);
|
||||||
|
keyChainButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.keychain;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
import javax.net.ssl.SSLServerSocketFactory;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class SecureWebServer {
|
||||||
|
|
||||||
|
// Log tag for this class
|
||||||
|
private static final String TAG = "SecureWebServer";
|
||||||
|
|
||||||
|
// File name of the image used in server response
|
||||||
|
private static final String EMBEDDED_IMAGE_FILENAME = "training-prof.png";
|
||||||
|
|
||||||
|
private SSLServerSocketFactory sssf;
|
||||||
|
private SSLServerSocket sss;
|
||||||
|
|
||||||
|
// A flag to control whether the web server should be kept running
|
||||||
|
private boolean isRunning = true;
|
||||||
|
|
||||||
|
// The base64 encoded image string used as an embedded image
|
||||||
|
private final String base64Image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebServer constructor.
|
||||||
|
*/
|
||||||
|
public SecureWebServer(Context ctx) {
|
||||||
|
try {
|
||||||
|
// Get an SSL context using the TLS protocol
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
|
||||||
|
// Get a key manager factory using the default algorithm
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory
|
||||||
|
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
|
|
||||||
|
// Load the PKCS12 key chain
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||||
|
FileInputStream fis = ctx.getAssets()
|
||||||
|
.openFd(KeyChainDemoActivity.PKCS12_FILENAME)
|
||||||
|
.createInputStream();
|
||||||
|
ks.load(fis, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
|
||||||
|
kmf.init(ks, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
|
||||||
|
|
||||||
|
// Initialize the SSL context
|
||||||
|
sslContext.init(kmf.getKeyManagers(), null, null);
|
||||||
|
|
||||||
|
// Create the SSL server socket factory
|
||||||
|
sssf = sslContext.getServerSocketFactory();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the base64 image string used in the server response
|
||||||
|
base64Image = createBase64Image(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method starts the web server listening to the port 8080
|
||||||
|
*/
|
||||||
|
protected void start() {
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Log.d(TAG, "Secure Web Server is starting up on port 8080");
|
||||||
|
try {
|
||||||
|
// Create the secure server socket
|
||||||
|
sss = (SSLServerSocket) sssf.createServerSocket(8080);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Waiting for connection");
|
||||||
|
while (isRunning) {
|
||||||
|
try {
|
||||||
|
// Wait for an SSL connection
|
||||||
|
Socket socket = sss.accept();
|
||||||
|
|
||||||
|
// Got a connection
|
||||||
|
Log.d(TAG, "Connected, sending data.");
|
||||||
|
|
||||||
|
BufferedReader in = new BufferedReader(
|
||||||
|
new InputStreamReader(socket.getInputStream()));
|
||||||
|
PrintWriter out = new PrintWriter(socket
|
||||||
|
.getOutputStream());
|
||||||
|
|
||||||
|
// Read the data until a blank line is reached which
|
||||||
|
// signifies the end of the client HTTP headers
|
||||||
|
String str = ".";
|
||||||
|
while (!str.equals(""))
|
||||||
|
str = in.readLine();
|
||||||
|
|
||||||
|
// Send a HTTP response
|
||||||
|
out.println("HTTP/1.0 200 OK");
|
||||||
|
out.println("Content-Type: text/html");
|
||||||
|
out.println("Server: Android KeyChainiDemo SSL Server");
|
||||||
|
// this blank line signals the end of the headers
|
||||||
|
out.println("");
|
||||||
|
// Send the HTML page
|
||||||
|
out.println("<H1>Welcome to Android!</H1>");
|
||||||
|
// Add an embedded Android image
|
||||||
|
out.println("<img src='data:image/png;base64," + base64Image + "'/>");
|
||||||
|
out.flush();
|
||||||
|
socket.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d(TAG, "Error: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method stops the SSL web server
|
||||||
|
*/
|
||||||
|
protected void stop() {
|
||||||
|
try {
|
||||||
|
// Break out from the infinite while loop in start()
|
||||||
|
isRunning = false;
|
||||||
|
|
||||||
|
// Close the socket
|
||||||
|
if (sss != null) {
|
||||||
|
sss.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method reads a binary image from the assets folder and returns the
|
||||||
|
* base64 encoded image string.
|
||||||
|
*
|
||||||
|
* @param ctx The service this web server is running in.
|
||||||
|
* @return String The base64 encoded image string or "" if there is an
|
||||||
|
* exception
|
||||||
|
*/
|
||||||
|
private String createBase64Image(Context ctx) {
|
||||||
|
BufferedInputStream bis;
|
||||||
|
try {
|
||||||
|
bis = new BufferedInputStream(ctx.getAssets().open(EMBEDDED_IMAGE_FILENAME));
|
||||||
|
byte[] embeddedImage = new byte[bis.available()];
|
||||||
|
bis.read(embeddedImage);
|
||||||
|
return Base64.encodeToString(embeddedImage, Base64.DEFAULT);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.keychain;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class SecureWebServerService extends Service {
|
||||||
|
|
||||||
|
// Log tag for this class
|
||||||
|
private static final String TAG = "SecureWebServerService";
|
||||||
|
|
||||||
|
// A special ID assigned to this on-going notification.
|
||||||
|
private static final int ONGOING_NOTIFICATION = 1248;
|
||||||
|
|
||||||
|
// A handle to the simple SSL web server
|
||||||
|
private SecureWebServer sws;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the SSL web server and set an on-going notification
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
sws = new SecureWebServer(this);
|
||||||
|
sws.start();
|
||||||
|
createNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the SSL web server and remove the on-going notification
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
sws.stop();
|
||||||
|
stopForeground(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return null as there is nothing to bind
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an on-going notification. It will stop the server when the user
|
||||||
|
* clicks on the notification.
|
||||||
|
*/
|
||||||
|
private void createNotification() {
|
||||||
|
Log.d(TAG, "Create an ongoing notification");
|
||||||
|
Intent notificationIntent = new Intent(this,
|
||||||
|
KeyChainDemoActivity.class);
|
||||||
|
notificationIntent.putExtra(KeyChainDemoActivity.EXTRA_STOP_SERVER,
|
||||||
|
true);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
|
||||||
|
notificationIntent, 0);
|
||||||
|
Notification notification = new Notification.Builder(this).
|
||||||
|
setContentTitle(getText(R.string.notification_title)).
|
||||||
|
setContentText(getText(R.string.notification_message)).
|
||||||
|
setSmallIcon(android.R.drawable.ic_media_play).
|
||||||
|
setTicker(getText(R.string.ticker_text)).
|
||||||
|
setOngoing(true).
|
||||||
|
setContentIntent(pendingIntent).
|
||||||
|
getNotification();
|
||||||
|
startForeground(ONGOING_NOTIFICATION, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user