Merge "Pass URLs to package verifiers" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 5e0b2e6..c751052 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22503,6 +22503,17 @@
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt, android.text.TextPaint);
}
+ public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
+ ctor public LocaleSpan(java.util.Locale);
+ ctor public LocaleSpan(android.os.Parcel);
+ method public int describeContents();
+ method public java.util.Locale getLocale();
+ method public int getSpanTypeId();
+ method public void updateDrawState(android.text.TextPaint);
+ method public void updateMeasureState(android.text.TextPaint);
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
public class MaskFilterSpan extends android.text.style.CharacterStyle implements android.text.style.UpdateAppearance {
ctor public MaskFilterSpan(android.graphics.MaskFilter);
method public android.graphics.MaskFilter getMaskFilter();
@@ -26936,6 +26947,7 @@
method public synchronized boolean getUseWideViewPort();
method public deprecated synchronized int getUserAgent();
method public synchronized java.lang.String getUserAgentString();
+ method public static java.lang.String getDefaultUserAgent(android.content.Context);
method public void setAllowContentAccess(boolean);
method public void setAllowFileAccess(boolean);
method public abstract void setAllowFileAccessFromFileURLs(boolean);
@@ -29303,6 +29315,7 @@
method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int);
method public final android.content.res.ColorStateList getTextColors();
method public static android.content.res.ColorStateList getTextColors(android.content.Context, android.content.res.TypedArray);
+ method public java.util.Locale getTextLocale();
method public float getTextScaleX();
method public float getTextSize();
method public int getTotalPaddingBottom();
@@ -29405,6 +29418,7 @@
method public void setTextIsSelectable(boolean);
method public final void setTextKeepState(java.lang.CharSequence);
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
+ method public void setTextLocale(java.util.Locale);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSize(int, float);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a3880f3..4740e53c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -82,6 +82,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.renderscript.RenderScript;
+import android.security.AndroidKeyStoreProvider;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
@@ -96,6 +97,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
+import java.security.Security;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -4810,6 +4812,8 @@
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
+ Security.addProvider(new AndroidKeyStoreProvider());
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 00438a1..d59fa6a 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -184,8 +184,17 @@
if (!TextUtils.equals(apnType, mApnType)) {
return;
}
- mNetworkInfo.setSubtype(TelephonyManager.getDefault().getNetworkType(),
- TelephonyManager.getDefault().getNetworkTypeName());
+
+ int oldSubtype = mNetworkInfo.getSubtype();
+ int newSubType = TelephonyManager.getDefault().getNetworkType();
+ String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
+ mNetworkInfo.setSubtype(newSubType, subTypeName);
+ if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
+ Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
+ oldSubtype, 0, mNetworkInfo);
+ msg.sendToTarget();
+ }
+
PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
intent.getStringExtra(PhoneConstants.STATE_KEY));
String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 313c174..eae89f1c 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -63,6 +63,12 @@
public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
/**
+ * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED
+ * msg.obj = NetworkInfo object
+ */
+ public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7;
+
+ /**
* -------------------------------------------------------------
* Control Interface
* -------------------------------------------------------------
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c103aa..0941d71 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -53,8 +53,6 @@
public static native int setPermissions(String file, int mode, int uid, int gid);
- public static native int setUMask(int mask);
-
/** returns the FAT file system volume ID for the volume mounted
* at the given mount point, or -1 for failure
* @param mountPoint point for FAT volume
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 83a70f3f..987062a 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -27,6 +27,7 @@
import android.text.style.EasyEditSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
+import android.text.style.LocaleSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
@@ -587,6 +588,8 @@
public static final int SUGGESTION_RANGE_SPAN = 21;
/** @hide */
public static final int EASY_EDIT_SPAN = 22;
+ /** @hide */
+ public static final int LOCALE_SPAN = 23;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -754,6 +757,10 @@
readSpan(p, sp, new EasyEditSpan());
break;
+ case LOCALE_SPAN:
+ readSpan(p, sp, new LocaleSpan(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
new file mode 100644
index 0000000..a12c42f
--- /dev/null
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package android.text.style;
+
+import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import java.util.Locale;
+
+/**
+ * Changes the {@link Locale} of the text to which the span is attached.
+ */
+public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan {
+ private final Locale mLocale;
+
+ /**
+ * Creates a LocaleSpan.
+ * @param locale The {@link Locale} of the text to which the span is
+ * attached.
+ */
+ public LocaleSpan(Locale locale) {
+ mLocale = locale;
+ }
+
+ public LocaleSpan(Parcel src) {
+ mLocale = new Locale(src.readString(), src.readString(), src.readString());
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.LOCALE_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mLocale.getLanguage());
+ dest.writeString(mLocale.getCountry());
+ dest.writeString(mLocale.getVariant());
+ }
+
+ /**
+ * Returns the {@link Locale}.
+ *
+ * @return The {@link Locale} for this span.
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ apply(ds, mLocale);
+ }
+
+ @Override
+ public void updateMeasureState(TextPaint paint) {
+ apply(paint, mLocale);
+ }
+
+ private static void apply(Paint paint, Locale locale) {
+ paint.setTextLocale(locale);
+ }
+}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 193e98d..074f910 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.content.Context;
import android.os.Message;
import android.os.Build;
@@ -1226,6 +1227,18 @@
}
/**
+ * Returns the default User-Agent used by a WebView.
+ * An instance of WebView could use a different User-Agent if a call
+ * is made to {@link WebSettings#setUserAgent(int)} or
+ * {@link WebSettings#setUserAgentString(String)}.
+ *
+ * @param context a Context object used to access application assets
+ */
+ public static String getDefaultUserAgent(Context context) {
+ return WebView.getFactory().getDefaultUserAgent(context);
+ }
+
+ /**
* Tells the WebView whether it needs to set a node to have focus when
* {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The
* default value is true.
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 66651f7..eac1141 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -374,6 +374,21 @@
synchronized(sLockForLocaleSettings) {
locale = sLocale;
}
+ return getDefaultUserAgentForLocale(mContext, locale);
+ }
+
+ /**
+ * Returns the default User-Agent used by a WebView.
+ * An instance of WebView could use a different User-Agent if a call
+ * is made to {@link WebSettings#setUserAgent(int)} or
+ * {@link WebSettings#setUserAgentString(String)}.
+ *
+ * @param context a Context object used to access application assets
+ * @param locale The Locale to use in the User-Agent string.
+ * @see WebViewFactoryProvider#getDefaultUserAgent(Context)
+ * @see WebView#getDefaultUserAgent(Context)
+ */
+ public static String getDefaultUserAgentForLocale(Context context, Locale locale) {
StringBuffer buffer = new StringBuffer();
// Add version
final String version = Build.VERSION.RELEASE;
@@ -417,9 +432,9 @@
buffer.append(" Build/");
buffer.append(id);
}
- String mobile = mContext.getResources().getText(
+ String mobile = context.getResources().getText(
com.android.internal.R.string.web_user_agent_target_content).toString();
- final String base = mContext.getResources().getText(
+ final String base = context.getResources().getText(
com.android.internal.R.string.web_user_agent).toString();
return String.format(base, buffer, mobile);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 436762d..4c5699b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1821,7 +1821,7 @@
}
}
- private static synchronized WebViewFactoryProvider getFactory() {
+ static synchronized WebViewFactoryProvider getFactory() {
// For now the main purpose of this function (and the factory abstration) is to keep
// us honest and minimize usage of WebViewClassic internals when binding the proxy.
checkThread();
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index c816d5b..52048db 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -132,6 +132,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
@@ -1308,6 +1309,12 @@
public WebViewDatabase getWebViewDatabase(Context context) {
return WebViewDatabaseClassic.getInstance(context);
}
+
+ @Override
+ public String getDefaultUserAgent(Context context) {
+ return WebSettingsClassic.getDefaultUserAgentForLocale(context,
+ Locale.getDefault());
+ }
}
private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 1d302f1..b1d42aa 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -93,4 +93,14 @@
* @return the singleton WebViewDatabase instance
*/
WebViewDatabase getWebViewDatabase(Context context);
+
+ /**
+ * Returns the default User-Agent used by a WebView.
+ * An instance of WebView could use a different User-Agent if a call
+ * is made to {@link WebSettings#setUserAgent(int)} or
+ * {@link WebSettings#setUserAgentString(String)}.
+ *
+ * @param context a Context object used to access application assets
+ */
+ String getDefaultUserAgent(Context context);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5be9899..f502de4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2203,6 +2203,27 @@
}
/**
+ * Get the default {@link Locale} of the text in this TextView.
+ * @return the default {@link Locale} of the text in this TextView.
+ */
+ public Locale getTextLocale() {
+ return mTextPaint.getTextLocale();
+ }
+
+ /**
+ * Set the default {@link Locale} of the text in this TextView to the given value. This value
+ * is used to choose appropriate typefaces for ambiguous characters. Typically used for CJK
+ * locales to disambiguate Hanzi/Kanji/Hanja characters.
+ *
+ * @param locale the {@link Locale} for drawing text, must not be null.
+ *
+ * @see Paint#setTextLocale
+ */
+ public void setTextLocale(Locale locale) {
+ mTextPaint.setTextLocale(locale);
+ }
+
+ /**
* @return the size (in pixels) of the default text size in this TextView.
*/
@ViewDebug.ExportedProperty(category = "text")
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6ad67c3..9e43749 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,16 +16,17 @@
package com.android.internal.os;
+import static libcore.io.OsConstants.S_IRWXG;
+import static libcore.io.OsConstants.S_IRWXO;
+
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket;
import android.os.Debug;
-import android.os.FileUtils;
import android.os.Process;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.EventLog;
import android.util.Log;
@@ -33,6 +34,7 @@
import dalvik.system.Zygote;
import libcore.io.IoUtils;
+import libcore.io.Libcore;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -452,7 +454,7 @@
closeServerSocket();
// set umask to 0077 so new files and directories will default to owner-only permissions.
- FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);
+ Libcore.os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a65262c..edc9732 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -22,6 +22,7 @@
#include "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
+#include <ScopedUtfChars.h>
#include "SkBlurDrawLooper.h"
#include "SkColorFilter.h"
@@ -30,6 +31,7 @@
#include "SkShader.h"
#include "SkTypeface.h"
#include "SkXfermode.h"
+#include "unicode/uloc.h"
#include "unicode/ushape.h"
#include "TextLayout.h"
@@ -254,11 +256,51 @@
obj->setTextAlign(align);
}
+ // generate bcp47 identifier for the supplied locale
+ static void toLanguageTag(char* output, size_t outSize,
+ const char* locale) {
+ if (output == NULL || outSize <= 0) {
+ return;
+ }
+ if (locale == NULL) {
+ output[0] = '\0';
+ return;
+ }
+ char canonicalChars[ULOC_FULLNAME_CAPACITY];
+ UErrorCode uErr = U_ZERO_ERROR;
+ uloc_canonicalize(locale, canonicalChars, ULOC_FULLNAME_CAPACITY,
+ &uErr);
+ if (U_SUCCESS(uErr)) {
+ char likelyChars[ULOC_FULLNAME_CAPACITY];
+ uErr = U_ZERO_ERROR;
+ uloc_addLikelySubtags(canonicalChars, likelyChars,
+ ULOC_FULLNAME_CAPACITY, &uErr);
+ if (U_SUCCESS(uErr)) {
+ uErr = U_ZERO_ERROR;
+ uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr);
+ if (U_SUCCESS(uErr)) {
+ return;
+ } else {
+ ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars,
+ u_errorName(uErr));
+ }
+ } else {
+ ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s",
+ canonicalChars, u_errorName(uErr));
+ }
+ } else {
+ ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale,
+ u_errorName(uErr));
+ }
+ // unable to build a proper language identifier
+ output[0] = '\0';
+ }
+
static void setTextLocale(JNIEnv* env, jobject clazz, SkPaint* obj, jstring locale) {
- const char* localeArray = env->GetStringUTFChars(locale, NULL);
- SkString skLocale(localeArray);
- obj->setTextLocale(skLocale);
- env->ReleaseStringUTFChars(locale, localeArray);
+ ScopedUtfChars localeChars(env, locale);
+ char langTag[ULOC_FULLNAME_CAPACITY];
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
+ obj->setLanguage(SkLanguage(langTag));
}
static jfloat getTextSize(JNIEnv* env, jobject paint) {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 9b9b991..7abfcf1 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -19,6 +19,7 @@
#include "TextLayoutCache.h"
#include "TextLayout.h"
#include "SkFontHost.h"
+#include "SkTypeface_android.h"
#include <unicode/unistr.h>
#include <unicode/normlzr.h>
#include <unicode/uchar.h>
@@ -224,7 +225,7 @@
*/
TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0),
dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
- hinting(SkPaint::kNo_Hinting) {
+ hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() {
}
TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
@@ -237,6 +238,8 @@
textScaleX = paint->getTextScaleX();
flags = paint->getFlags();
hinting = paint->getHinting();
+ variant = paint->getFontVariant();
+ language = paint->getLanguage();
}
TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
@@ -251,7 +254,9 @@
textSkewX(other.textSkewX),
textScaleX(other.textScaleX),
flags(other.flags),
- hinting(other.hinting) {
+ hinting(other.hinting),
+ variant(other.variant),
+ language(other.language) {
if (other.text) {
textCopy.setTo(other.text, other.contextCount);
}
@@ -288,6 +293,12 @@
deltaInt = lhs.dirFlags - rhs.dirFlags;
if (deltaInt) return (deltaInt);
+ deltaInt = lhs.variant - rhs.variant;
+ if (deltaInt) return (deltaInt);
+
+ if (lhs.language < rhs.language) return -1;
+ if (lhs.language > rhs.language) return +1;
+
return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
}
@@ -615,6 +626,8 @@
mShapingPaint.setTextScaleX(paint->getTextScaleX());
mShapingPaint.setFlags(paint->getFlags());
mShapingPaint.setHinting(paint->getHinting());
+ mShapingPaint.setFontVariant(paint->getFontVariant());
+ mShapingPaint.setLanguage(paint->getLanguage());
// Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
// into the shaperItem
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index f007f9a..64b33a0 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -32,7 +32,7 @@
#include <SkTemplates.h>
#include <SkUtils.h>
#include <SkAutoKern.h>
-#include "SkTypeface_android.h"
+#include <SkLanguage.h>
#include <unicode/ubidi.h>
#include <unicode/ushape.h>
@@ -102,6 +102,8 @@
SkScalar textScaleX;
uint32_t flags;
SkPaint::Hinting hinting;
+ SkPaint::FontVariant variant;
+ SkLanguage language;
inline const UChar* getText() const { return text ? text : textCopy.string(); }
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 82bfc36..a07f5b7 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -55,11 +55,6 @@
return chmod(file8.string(), mode) == 0 ? 0 : errno;
}
-jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
-{
- return umask(mask);
-}
-
jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
{
if (path == NULL) {
@@ -83,7 +78,6 @@
static const JNINativeMethod methods[] = {
{"setPermissions", "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
- {"setUMask", "(I)I", (void*)android_os_FileUtils_setUMask},
{"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
};
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 3d6c9d3..7bc172c 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -76,14 +76,6 @@
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
-include $(CLEAR_VARS)
-LOCAL_MODULE := fallback_fonts-ja.xml
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
droidsans_fallback_src := DroidSansFallbackFull.ttf
extra_font_files := \
DroidSans.ttf \
@@ -91,8 +83,7 @@
DroidSansEthiopic-Regular.ttf \
DroidSansTamil-Regular.ttf \
DroidSansTamil-Bold.ttf \
- MTLmr3m.ttf \
- fallback_fonts-ja.xml
+ MTLmr3m.ttf
endif # SMALLER_FONT_FOOTPRINT
################################
diff --git a/data/fonts/fallback_fonts-ja.xml b/data/fonts/fallback_fonts-ja.xml
deleted file mode 100644
index 82e3a38..0000000
--- a/data/fonts/fallback_fonts-ja.xml
+++ /dev/null
@@ -1,121 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Fallback Fonts
-
- This file specifies the fonts, and the priority order, that will be searched for any
- glyphs not handled by the default fonts specified in /system/etc/system_fonts.xml.
- Each entry consists of a family tag and a list of files (file names) which support that
- family. The fonts for each family are listed in the order of the styles that they
- handle (the order is: regular, bold, italic, and bold-italic). The order in which the
- families are listed in this file represents the order in which these fallback fonts
- will be searched for glyphs that are not supported by the default system fonts (which are
- found in /system/etc/system_fonts.xml).
-
- Note that there is not nameset for fallback fonts, unlike the fonts specified in
- system_fonts.xml. The ability to support specific names in fallback fonts may be supported
- in the future. For now, the lack of files entries here is an indicator to the system that
- these are fallback fonts, instead of default named system fonts.
-
- There is another optional file in /vendor/etc/fallback_fonts.xml. That file can be used to
- provide references to other font families that should be used in addition to the default
- fallback fonts. That file can also specify the order in which the fallback fonts should be
- searched, to ensure that a vendor-provided font will be used before another fallback font
- which happens to handle the same glyph.
-
- Han languages (Chinese, Japanese, and Korean) share a common range of unicode characters;
- their ordering in the fallback or vendor files gives priority to the first in the list.
- Locale-specific ordering can be configured by adding language and region codes to the end
- of the filename (e.g. /system/etc/fallback_fonts-ja.xml). When no region code is used,
- as with this example, all regions are matched. Use separate files for each supported locale.
- The standard fallback file (fallback_fonts.xml) is used when a locale does not have its own
- file. All fallback files must contain the same complete set of fonts; only their ordering
- can differ.
--->
-<familyset>
- <family>
- <fileset>
- <file variant="elegant">DroidNaskh-Regular.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file variant="compact">DroidNaskh-Regular-SystemUI.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansEthiopic-Regular.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansHebrew-Regular.ttf</file>
- <file>DroidSansHebrew-Bold.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansThai.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansArmenian.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansGeorgian.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansDevanagari-Regular.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansTamil-Regular.ttf</file>
- <file>DroidSansTamil-Bold.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>AnjaliNewLipi-light.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>Lohit-Bengali.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>Lohit-Kannada.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>AndroidEmoji.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>MTLmr3m.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
- <file>DroidSansFallback.ttf</file>
- </fileset>
- </family>
- <!--
- Fonts below this point have problematic glyphs and should not be moved
- higher in the fallback list until those glyphs have been fixed.
- -->
- <family>
- <fileset>
- <file>Lohit-Telugu.ttf</file> <!-- masks U+FFBC-10007 -->
- </fileset>
- </family>
-</familyset>
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 758adb5..2c9a0c8 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -24,12 +24,9 @@
Han languages (Chinese, Japanese, and Korean) share a common range of unicode characters;
their ordering in the fallback or vendor files gives priority to the first in the list.
- Locale-specific ordering can be configured by adding language and region codes to the end
- of the filename (e.g. /system/etc/fallback_fonts-ja.xml). When no region code is used,
- as with this example, all regions are matched. Use separate files for each supported locale.
- The standard fallback file (fallback_fonts.xml) is used when a locale does not have its own
- file. All fallback files must contain the same complete set of fonts; only their ordering
- can differ.
+ Language-specific ordering can be configured by adding a BCP 47-style "lang" attribute to
+ a "file" element; fonts matching the language of text being drawn will be prioritised over
+ all others.
-->
<familyset>
<family>
@@ -106,7 +103,7 @@
</family>
<family>
<fileset>
- <file>MTLmr3m.ttf</file>
+ <file lang="ja">MTLmr3m.ttf</file>
</fileset>
</family>
<!--
diff --git a/data/fonts/vendor_fonts.xml b/data/fonts/vendor_fonts.xml
index 5850f94..8690ee1 100644
--- a/data/fonts/vendor_fonts.xml
+++ b/data/fonts/vendor_fonts.xml
@@ -7,8 +7,7 @@
that in your makefile, this directory should be referenced as $(TARGET_COPY_OUT_VENDOR)/etc/:
PRODUCT_COPY_FILES += \
- frameworks/base/data/fonts/vendor_fonts.xml:$(TARGET_COPY_OUT_VENDOR)/etc/fallback_fonts.xml \
- frameworks/base/data/fonts/vendor_fonts-ja.xml:$(TARGET_COPY_OUT_VENDOR)/etc/fallback_fonts-ja.xml
+ frameworks/base/data/fonts/vendor_fonts.xml:$(TARGET_COPY_OUT_VENDOR)/etc/fallback_fonts.xml
For example, vendors might want to build configurations for locales that are
better served by fonts which either handle glyphs not supported in the default fonts or which
@@ -32,32 +31,9 @@
Han languages (Chinese, Japanese, and Korean) share a common range of unicode characters;
their ordering in the fallback or vendor files gives priority to the first in the list.
- Locale-specific ordering can be configured by adding language and region codes to the end
- of the filename (e.g. /vendor/etc/fallback_fonts-ja.xml). When no region code is used,
- as with this example, all regions are matched. Use separate files for each supported locale.
- The standard fallback file (fallback_fonts.xml) is used when a locale does not have its own
- file. All fallback files must contain the same complete set of fonts; only their ordering
- can differ. For example, on a device supporting Japanese, but with English as the default,
- /vendor/etc/fallback_fonts.xml might contain:
-
- <familyset>
- <family>
- <fileset>
- <file>DroidSansJapanese.ttf</file>
- </fileset>
- </family>
- </familyset>
-
- placing the Japanese font at the end of the fallback sequence for English, with a corresponding
- /system/vendor/etc/fallback_fonts-ja.xml, placing it at the front of the list.
-
- <familyset>
- <family order="0">
- <fileset>
- <file>DroidSansJapanese.ttf</file>
- </fileset>
- </family>
- </familyset>
+ Language-specific ordering can be configured by adding a BCP 47-style "lang" attribute to
+ a "file" element; fonts matching the language of text being drawn will be prioritised over
+ all others.
The sample configuration below is an example of how one might provide two families of fonts
that get inserted at the first and second (0 and 1) position in the overall fallback fonts.
@@ -82,4 +58,4 @@
</fileset>
</family>
</familyset>
--->
\ No newline at end of file
+--->
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
new file mode 100644
index 0000000..a629f8d
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ */
+
+package android.security;
+
+import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
+
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A java.security.KeyStore interface for the Android KeyStore. This class is
+ * hidden from the Android API, but an instance of it can be created via the
+ * {@link java.security.KeyStore#getInstance(String)
+ * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
+ * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
+ * <p>
+ * This is built on top of Android's keystore daemon. The convention of alias
+ * use is:
+ * <p>
+ * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
+ * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
+ * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
+ * entry which will have the rest of the chain concatenated in BER format.
+ * <p>
+ * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
+ * with a single certificate.
+ *
+ * @hide
+ */
+public class AndroidKeyStore extends KeyStoreSpi {
+ public static final String NAME = "AndroidKeyStore";
+
+ private android.security.KeyStore mKeyStore;
+
+ @Override
+ public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
+ UnrecoverableKeyException {
+ if (!isKeyEntry(alias)) {
+ return null;
+ }
+
+ final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+ try {
+ return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
+ } catch (InvalidKeyException e) {
+ UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
+ t.initCause(e);
+ throw t;
+ }
+ }
+
+ @Override
+ public Certificate[] engineGetCertificateChain(String alias) {
+ final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
+ if (leaf == null) {
+ return null;
+ }
+
+ final Certificate[] caList;
+
+ final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ if (caBytes != null) {
+ final Collection<X509Certificate> caChain = toCertificates(caBytes);
+
+ caList = new Certificate[caChain.size() + 1];
+
+ final Iterator<X509Certificate> it = caChain.iterator();
+ int i = 1;
+ while (it.hasNext()) {
+ caList[i++] = it.next();
+ }
+ } else {
+ caList = new Certificate[1];
+ }
+
+ caList[0] = leaf;
+
+ return caList;
+ }
+
+ @Override
+ public Certificate engineGetCertificate(String alias) {
+ byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ if (certificate != null) {
+ return toCertificate(certificate);
+ }
+
+ certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ if (certificate != null) {
+ return toCertificate(certificate);
+ }
+
+ return null;
+ }
+
+ private static X509Certificate toCertificate(byte[] bytes) {
+ try {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certFactory
+ .generateCertificate(new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ Log.w(NAME, "Couldn't parse certificate in keystore", e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Collection<X509Certificate> toCertificates(byte[] bytes) {
+ try {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (Collection<X509Certificate>) certFactory
+ .generateCertificates(new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ Log.w(NAME, "Couldn't parse certificates in keystore", e);
+ return new ArrayList<X509Certificate>();
+ }
+ }
+
+ private Date getModificationDate(String alias) {
+ final long epochMillis = mKeyStore.getmtime(alias);
+ if (epochMillis == -1L) {
+ return null;
+ }
+
+ return new Date(epochMillis);
+ }
+
+ @Override
+ public Date engineGetCreationDate(String alias) {
+ Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
+ if (d != null) {
+ return d;
+ }
+
+ d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
+ if (d != null) {
+ return d;
+ }
+
+ return getModificationDate(Credentials.CA_CERTIFICATE + alias);
+ }
+
+ @Override
+ public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+ throws KeyStoreException {
+ if ((password != null) && (password.length > 0)) {
+ throw new KeyStoreException("entries cannot be protected with passwords");
+ }
+
+ if (key instanceof PrivateKey) {
+ setPrivateKeyEntry(alias, (PrivateKey) key, chain);
+ } else {
+ throw new KeyStoreException("Only PrivateKeys are supported");
+ }
+ }
+
+ private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain)
+ throws KeyStoreException {
+ // Make sure the PrivateKey format is the one we support.
+ final String keyFormat = key.getFormat();
+ if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+ throw new KeyStoreException(
+ "Only PrivateKeys that can be encoded into PKCS#8 are supported");
+ }
+
+ // Make sure we can actually encode the key.
+ final byte[] keyBytes = key.getEncoded();
+ if (keyBytes == null) {
+ throw new KeyStoreException("PrivateKey has no encoding");
+ }
+
+ // Make sure the chain exists since this is a PrivateKey
+ if ((chain == null) || (chain.length == 0)) {
+ throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
+ }
+
+ // Do chain type checking.
+ X509Certificate[] x509chain = new X509Certificate[chain.length];
+ for (int i = 0; i < chain.length; i++) {
+ if (!"X.509".equals(chain[i].getType())) {
+ throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
+ + i);
+ }
+
+ if (!(chain[i] instanceof X509Certificate)) {
+ throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
+ + i);
+ }
+
+ x509chain[i] = (X509Certificate) chain[i];
+ }
+
+ final byte[] userCertBytes;
+ try {
+ userCertBytes = x509chain[0].getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new KeyStoreException("Couldn't encode certificate #1", e);
+ }
+
+ /*
+ * If we have a chain, store it in the CA certificate slot for this
+ * alias as concatenated DER-encoded certificates. These can be
+ * deserialized by {@link CertificateFactory#generateCertificates}.
+ */
+ final byte[] chainBytes;
+ if (chain.length > 1) {
+ /*
+ * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
+ * so we only need the certificates starting at index 1.
+ */
+ final byte[][] certsBytes = new byte[x509chain.length - 1][];
+ int totalCertLength = 0;
+ for (int i = 0; i < certsBytes.length; i++) {
+ try {
+ certsBytes[i] = x509chain[i + 1].getEncoded();
+ totalCertLength += certsBytes[i].length;
+ } catch (CertificateEncodingException e) {
+ throw new KeyStoreException("Can't encode Certificate #" + i, e);
+ }
+ }
+
+ /*
+ * Serialize this into one byte array so we can later call
+ * CertificateFactory#generateCertificates to recover them.
+ */
+ chainBytes = new byte[totalCertLength];
+ int outputOffset = 0;
+ for (int i = 0; i < certsBytes.length; i++) {
+ final int certLength = certsBytes[i].length;
+ System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
+ outputOffset += certLength;
+ certsBytes[i] = null;
+ }
+ } else {
+ chainBytes = null;
+ }
+
+ /*
+ * Make sure we clear out all the types we know about before trying to
+ * write.
+ */
+ deleteAllTypesForAlias(alias);
+
+ if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
+ throw new KeyStoreException("Couldn't put private key in keystore");
+ } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes)) {
+ throw new KeyStoreException("Couldn't put certificate #1 in keystore");
+ } else if (chainBytes != null
+ && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes)) {
+ throw new KeyStoreException("Couldn't put certificate chain in keystore");
+ }
+ }
+
+ @Override
+ public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
+ throws KeyStoreException {
+ throw new RuntimeException("Operation not supported because key encoding is unknown");
+ }
+
+ @Override
+ public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
+ if (isKeyEntry(alias)) {
+ throw new KeyStoreException("Entry exists and is not a trusted certificate");
+ }
+
+ final byte[] encoded;
+ try {
+ encoded = cert.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new KeyStoreException(e);
+ }
+
+ if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded)) {
+ throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
+ }
+ }
+
+ @Override
+ public void engineDeleteEntry(String alias) throws KeyStoreException {
+ if (!deleteAllTypesForAlias(alias)) {
+ throw new KeyStoreException("No such entry " + alias);
+ }
+ }
+
+ /**
+ * Delete all types (private key, certificate, CA certificate) for a
+ * particular {@code alias}. All three can exist for any given alias.
+ * Returns {@code true} if there was at least one of those types.
+ */
+ private boolean deleteAllTypesForAlias(String alias) {
+ /*
+ * Make sure every type is deleted. There can be all three types, so
+ * don't use a conditional here.
+ */
+ return mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + alias)
+ | mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)
+ | mKeyStore.delete(Credentials.CA_CERTIFICATE + alias);
+ }
+
+ private Set<String> getUniqueAliases() {
+ final String[] rawAliases = mKeyStore.saw("");
+ if (rawAliases == null) {
+ return new HashSet<String>();
+ }
+
+ final Set<String> aliases = new HashSet<String>(rawAliases.length);
+ for (String alias : rawAliases) {
+ final int idx = alias.indexOf('_');
+ if ((idx == -1) || (alias.length() <= idx)) {
+ Log.e(NAME, "invalid alias: " + alias);
+ continue;
+ }
+
+ aliases.add(new String(alias.substring(idx + 1)));
+ }
+
+ return aliases;
+ }
+
+ @Override
+ public Enumeration<String> engineAliases() {
+ return Collections.enumeration(getUniqueAliases());
+ }
+
+ @Override
+ public boolean engineContainsAlias(String alias) {
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
+ || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
+ || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ }
+
+ @Override
+ public int engineSize() {
+ return getUniqueAliases().size();
+ }
+
+ @Override
+ public boolean engineIsKeyEntry(String alias) {
+ return isKeyEntry(alias);
+ }
+
+ private boolean isKeyEntry(String alias) {
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
+ }
+
+ @Override
+ public boolean engineIsCertificateEntry(String alias) {
+ return !isKeyEntry(alias) && mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ }
+
+ @Override
+ public String engineGetCertificateAlias(Certificate cert) {
+ if (cert == null) {
+ return null;
+ }
+
+ final Set<String> nonCaEntries = new HashSet<String>();
+
+ /*
+ * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
+ * says to only compare the first certificate in the chain which is
+ * equivalent to the USER_CERTIFICATE prefix for the Android keystore
+ * convention.
+ */
+ final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
+ for (String alias : certAliases) {
+ final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+ if (certBytes == null) {
+ continue;
+ }
+
+ final Certificate c = toCertificate(certBytes);
+ nonCaEntries.add(alias);
+
+ if (cert.equals(c)) {
+ return alias;
+ }
+ }
+
+ /*
+ * Look at all the TrustedCertificateEntry types. Skip all the
+ * PrivateKeyEntry we looked at above.
+ */
+ final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
+ for (String alias : caAliases) {
+ if (nonCaEntries.contains(alias)) {
+ continue;
+ }
+
+ final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+ if (certBytes == null) {
+ continue;
+ }
+
+ final Certificate c = toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
+ if (cert.equals(c)) {
+ return alias;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void engineStore(OutputStream stream, char[] password) throws IOException,
+ NoSuchAlgorithmException, CertificateException {
+ throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
+ }
+
+ @Override
+ public void engineLoad(InputStream stream, char[] password) throws IOException,
+ NoSuchAlgorithmException, CertificateException {
+ if (stream != null) {
+ throw new IllegalArgumentException("InputStream not supported");
+ }
+
+ if (password != null) {
+ throw new IllegalArgumentException("password not supported");
+ }
+
+ // Unfortunate name collision.
+ mKeyStore = android.security.KeyStore.getInstance();
+ }
+
+}
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
new file mode 100644
index 0000000..df22f58
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.security;
+
+import java.security.Provider;
+
+/**
+ * A provider focused on providing JCA interfaces for the Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreProvider extends Provider {
+ public static final String PROVIDER_NAME = "AndroidKeyStoreProvider";
+
+ public AndroidKeyStoreProvider() {
+ super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
+
+ put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
+ }
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index f49c429..4637991 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -26,6 +26,7 @@
import java.nio.charset.Charsets;
import java.nio.charset.ModifiedUtf8;
import java.util.ArrayList;
+import java.util.Date;
/**
* @hide This should not be made public in its present form because it
@@ -228,6 +229,23 @@
return ungrant(getKeyBytes(key), getUidBytes(uid));
}
+ private long getmtime(byte[] key) {
+ final ArrayList<byte[]> values = execute('c', key);
+ if (values == null || values.isEmpty()) {
+ return -1L;
+ }
+
+ return Long.parseLong(new String(values.get(0))) * 1000L;
+ }
+
+ /**
+ * Returns the last modification time of the key in milliseconds since the
+ * epoch. Will return -1L if the key could not be found or other error.
+ */
+ public long getmtime(String key) {
+ return getmtime(getKeyBytes(key));
+ }
+
public int getLastError() {
return mError;
}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
new file mode 100644
index 0000000..bff01b8
--- /dev/null
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -0,0 +1,1383 @@
+/*
+ * 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.
+ */
+
+package android.security;
+
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+public class AndroidKeyStoreTest extends AndroidTestCase {
+ private android.security.KeyStore mAndroidKeyStore;
+
+ private java.security.KeyStore mKeyStore;
+
+ private static final String TEST_ALIAS_1 = "test1";
+
+ private static final String TEST_ALIAS_2 = "test2";
+
+ private static final String TEST_ALIAS_3 = "test3";
+
+ /*
+ * The keys and certificates below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_CA_1 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
+ (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
+ (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
+ (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
+ (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
+ (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+ (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
+ (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
+ (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
+ (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
+ (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
+ (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
+ (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
+ (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
+ (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
+ (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
+ (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
+ (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
+ (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
+ (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
+ (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
+ (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
+ (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
+ (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+ (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
+ (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
+ (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
+ (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
+ (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
+ (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
+ (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
+ (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
+ (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
+ (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
+ (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
+ (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
+ (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
+ (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
+ (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
+ (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
+ (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
+ (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
+ (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
+ (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
+ (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
+ (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
+ (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
+ (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+ (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
+ (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
+ (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
+ (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
+ (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
+ (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
+ (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
+ (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
+ (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
+ (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
+ (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
+ (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
+ (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+ (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+ (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+ (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+ (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+ (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+ (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
+ (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
+ (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
+ (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
+ (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+ (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
+ (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
+ (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
+ (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
+ (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
+ (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
+ (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
+ (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
+ (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
+ (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
+ (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
+ (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
+ (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
+ (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
+ (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
+ (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
+ (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
+ (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
+ (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
+ (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
+ (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
+ (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
+ (byte) 0xf1, (byte) 0x61
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+ (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+ (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+ (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+ (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+ (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+ (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+ (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+ (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+ (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+ (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+ (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+ (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+ (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+ (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+ (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+ (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+ (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+ (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+ (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+ (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+ (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+ (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+ (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+ (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+ (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+ (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+ (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+ (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+ (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+ (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+ (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+ (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+ (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+ (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+ (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+ (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+ (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+ (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+ (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+ (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+ (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+ (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+ (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+ (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+ (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+ (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+ (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+ (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+ (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+ (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+ (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+ (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+ (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+ (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+ (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+ (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+ (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+ (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+ (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+ (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+ (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+ (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+ (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+ (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+ (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+ (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+ (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+ (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+ (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+ (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+ (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+ (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+ (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+ (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+ (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+ (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+ (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+ (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+ (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+ (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+ (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+ (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+ (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+ (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+ (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+ (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+ (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+ (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+ (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+ (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+ (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+ (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+ (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+ (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+ (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_USER_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x95, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xfe, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+ (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+ (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+ (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+ (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+ (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+ (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+ (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30, (byte) 0x1e,
+ (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32, (byte) 0x30, (byte) 0x38,
+ (byte) 0x31, (byte) 0x34, (byte) 0x32, (byte) 0x33, (byte) 0x32, (byte) 0x35,
+ (byte) 0x34, (byte) 0x38, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32,
+ (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x32,
+ (byte) 0x33, (byte) 0x32, (byte) 0x35, (byte) 0x34, (byte) 0x38, (byte) 0x5a,
+ (byte) 0x30, (byte) 0x55, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+ (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+ (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31, (byte) 0x1b,
+ (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e, (byte) 0x64,
+ (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x54,
+ (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x61,
+ (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x31, (byte) 0x1c, (byte) 0x30,
+ (byte) 0x1a, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+ (byte) 0x13, (byte) 0x13, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76,
+ (byte) 0x65, (byte) 0x72, (byte) 0x31, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
+ (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
+ (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+ (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+ (byte) 0x81, (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6,
+ (byte) 0x5b, (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c,
+ (byte) 0x66, (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86,
+ (byte) 0x8a, (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3,
+ (byte) 0x02, (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08,
+ (byte) 0xf3, (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04,
+ (byte) 0x6d, (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f,
+ (byte) 0x67, (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c,
+ (byte) 0xcb, (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30,
+ (byte) 0xe2, (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5,
+ (byte) 0x79, (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b,
+ (byte) 0xce, (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb,
+ (byte) 0x08, (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff,
+ (byte) 0x3b, (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9,
+ (byte) 0xc4, (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29,
+ (byte) 0x0d, (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b,
+ (byte) 0x23, (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78,
+ (byte) 0x08, (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5,
+ (byte) 0xf1, (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19,
+ (byte) 0xb4, (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03,
+ (byte) 0x16, (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce,
+ (byte) 0x9e, (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03,
+ (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x7b, (byte) 0x30,
+ (byte) 0x79, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+ (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86,
+ (byte) 0x48, (byte) 0x01, (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01,
+ (byte) 0x0d, (byte) 0x04, (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f,
+ (byte) 0x70, (byte) 0x65, (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c,
+ (byte) 0x20, (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43,
+ (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+ (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04,
+ (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x32, (byte) 0xa1, (byte) 0x1e,
+ (byte) 0x6b, (byte) 0x69, (byte) 0x04, (byte) 0xfe, (byte) 0xb3, (byte) 0xcd,
+ (byte) 0xf8, (byte) 0xbb, (byte) 0x14, (byte) 0xcd, (byte) 0xff, (byte) 0xd4,
+ (byte) 0x16, (byte) 0xc3, (byte) 0xab, (byte) 0x44, (byte) 0x2f, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23,
+ (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14,
+ (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60,
+ (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c,
+ (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e,
+ (byte) 0x5d, (byte) 0x51, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x46, (byte) 0x42, (byte) 0xef,
+ (byte) 0x56, (byte) 0x89, (byte) 0x78, (byte) 0x90, (byte) 0x38, (byte) 0x24,
+ (byte) 0x9f, (byte) 0x8c, (byte) 0x7a, (byte) 0xce, (byte) 0x7a, (byte) 0xa5,
+ (byte) 0xb5, (byte) 0x1e, (byte) 0x74, (byte) 0x96, (byte) 0x34, (byte) 0x49,
+ (byte) 0x8b, (byte) 0xed, (byte) 0x44, (byte) 0xb3, (byte) 0xc9, (byte) 0x05,
+ (byte) 0xd7, (byte) 0x48, (byte) 0x55, (byte) 0x52, (byte) 0x59, (byte) 0x15,
+ (byte) 0x0b, (byte) 0xaa, (byte) 0x16, (byte) 0x86, (byte) 0xd2, (byte) 0x8e,
+ (byte) 0x16, (byte) 0x99, (byte) 0xe8, (byte) 0x5f, (byte) 0x11, (byte) 0x71,
+ (byte) 0x42, (byte) 0x55, (byte) 0xd1, (byte) 0xc4, (byte) 0x6f, (byte) 0x2e,
+ (byte) 0xa9, (byte) 0x64, (byte) 0x6f, (byte) 0xd8, (byte) 0xfd, (byte) 0x43,
+ (byte) 0x13, (byte) 0x24, (byte) 0xaa, (byte) 0x67, (byte) 0xe6, (byte) 0xf5,
+ (byte) 0xca, (byte) 0x80, (byte) 0x5e, (byte) 0x3a, (byte) 0x3e, (byte) 0xcc,
+ (byte) 0x4f, (byte) 0xba, (byte) 0x87, (byte) 0xe6, (byte) 0xae, (byte) 0xbf,
+ (byte) 0x8f, (byte) 0xd5, (byte) 0x28, (byte) 0x38, (byte) 0x58, (byte) 0x30,
+ (byte) 0x24, (byte) 0xf6, (byte) 0x53, (byte) 0x5b, (byte) 0x41, (byte) 0x53,
+ (byte) 0xe6, (byte) 0x45, (byte) 0xbc, (byte) 0xbe, (byte) 0xe6, (byte) 0xbb,
+ (byte) 0x5d, (byte) 0xd8, (byte) 0xa7, (byte) 0xf9, (byte) 0x64, (byte) 0x99,
+ (byte) 0x04, (byte) 0x43, (byte) 0x75, (byte) 0xd7, (byte) 0x2d, (byte) 0x32,
+ (byte) 0x0a, (byte) 0x94, (byte) 0xaf, (byte) 0x06, (byte) 0x34, (byte) 0xae,
+ (byte) 0x46, (byte) 0xbd, (byte) 0xda, (byte) 0x00, (byte) 0x0e, (byte) 0x25,
+ (byte) 0xc2, (byte) 0xf7, (byte) 0xc9, (byte) 0xc3, (byte) 0x65, (byte) 0xd2,
+ (byte) 0x08, (byte) 0x41, (byte) 0x0a, (byte) 0xf3, (byte) 0x72
+ };
+
+ /**
+ * The amount of time to allow before and after expected time for variance
+ * in timing tests.
+ */
+ private static final long SLOP_TIME_MILLIS = 15000L;
+
+ @Override
+ protected void setUp() throws Exception {
+ mAndroidKeyStore = android.security.KeyStore.getInstance();
+
+ assertTrue(mAndroidKeyStore.reset());
+
+ assertEquals(android.security.KeyStore.State.UNINITIALIZED, mAndroidKeyStore.state());
+
+ assertTrue(mAndroidKeyStore.password("1111"));
+
+ assertEquals(android.security.KeyStore.State.UNLOCKED, mAndroidKeyStore.state());
+
+ assertEquals(0, mAndroidKeyStore.saw("").length);
+
+ mKeyStore = java.security.KeyStore.getInstance(AndroidKeyStore.NAME);
+ }
+
+ private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
+ final Enumeration<String> aliases = mKeyStore.aliases();
+ int count = 0;
+
+ final Set<String> expectedSet = new HashSet<String>();
+ expectedSet.addAll(Arrays.asList(expectedAliases));
+
+ while (aliases.hasMoreElements()) {
+ count++;
+ final String alias = aliases.nextElement();
+ assertTrue("The alias should be in the expected set", expectedSet.contains(alias));
+ expectedSet.remove(alias);
+ }
+ assertTrue("The expected set and actual set should be exactly equal", expectedSet.isEmpty());
+ assertEquals("There should be the correct number of keystore entries",
+ expectedAliases.length, count);
+ }
+
+ public void testKeyStore_Aliases_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertAliases(new String[] {});
+
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+ }
+
+ public void testKeyStore_Aliases_NotInitialized_Failure() throws Exception {
+ try {
+ mKeyStore.aliases();
+ fail("KeyStore should throw exception when not initialized");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_ContainsAliases_PrivateAndCA_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertAliases(new String[] {});
+
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1));
+
+ assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+
+ assertFalse("Should not contain unadded certificate alias",
+ mKeyStore.containsAlias(TEST_ALIAS_3));
+ }
+
+ public void testKeyStore_ContainsAliases_CAOnly_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+ }
+
+ public void testKeyStore_ContainsAliases_NonExistent_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_DeleteEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ // TEST_ALIAS_2
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ // TEST_ALIAS_3
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_1);
+
+ assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_3);
+
+ assertAliases(new String[] { TEST_ALIAS_2 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_2);
+
+ assertAliases(new String[] { });
+ }
+
+ public void testKeyStore_DeleteEntry_EmptyStore_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ try {
+ mKeyStore.deleteEntry(TEST_ALIAS_1);
+ fail("Should throw KeyStoreException with non-existent alias");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_DeleteEntry_NonExistent_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ try {
+ mKeyStore.deleteEntry(TEST_ALIAS_2);
+ fail("Should throw KeyStoreException with non-existent alias");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_GetCertificate_Single_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ assertNull("Certificate should not exist in keystore",
+ mKeyStore.getCertificate(TEST_ALIAS_2));
+
+ Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+ assertNotNull("Retrieved certificate should not be null", retrieved);
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
+ }
+
+ public void testKeyStore_GetCertificate_NonExist_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("Certificate should not exist in keystore",
+ mKeyStore.getCertificate(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_GetCertificateAlias_CAEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ // Insert TrustedCertificateEntry with CA name
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ // Insert PrivateKeyEntry that uses the same CA
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_2,
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_NonExist_Empty_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_NonExist_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate userCert = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ assertNull("Stored certificate alias should be found",
+ mKeyStore.getCertificateAlias(userCert));
+ }
+
+ public void testKeyStore_GetCertificateChain_SingleLength_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ Certificate[] expected = new Certificate[2];
+ expected[0] = cf.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expected[1] = cf.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ Certificate[] actual = mKeyStore.getCertificateChain(TEST_ALIAS_1);
+
+ assertNotNull("Returned certificate chain should not be null", actual);
+ assertEquals("Returned certificate chain should be correct size", expected.length,
+ actual.length);
+ assertEquals("First certificate should be user certificate", expected[0], actual[0]);
+ assertEquals("Second certificate should be CA certificate", expected[1], actual[1]);
+
+ // Negative test when keystore is populated.
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateChain(TEST_ALIAS_2));
+ }
+
+ public void testKeyStore_GetCertificateChain_NonExist_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateChain(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetCreationDate_CAEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+ assertNotNull("Certificate should be found", actual);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetEntry_NullParams_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Entry should exist", entry);
+
+ assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, byte[] key, byte[] cert,
+ byte[] ca) throws Exception {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(key));
+
+ assertEquals("Returned PrivateKey should be what we inserted", expectedKey,
+ keyEntry.getPrivateKey());
+
+ CertificateFactory certFact = CertificateFactory.getInstance("X.509");
+ Certificate expectedCert = certFact.generateCertificate(new ByteArrayInputStream(cert));
+
+ assertEquals("Returned Certificate should be what we inserted", expectedCert,
+ keyEntry.getCertificate());
+
+ Certificate[] actualChain = keyEntry.getCertificateChain();
+
+ assertEquals("First certificate in chain should be user cert", expectedCert, actualChain[0]);
+
+ if (ca == null) {
+ assertEquals("Certificate chain should not include CAs", 1, actualChain.length);
+ } else {
+ @SuppressWarnings("unchecked")
+ Collection<Certificate> expectedChain = (Collection<Certificate>) certFact
+ .generateCertificates(new ByteArrayInputStream(ca));
+
+ int i = 1;
+ final Iterator<Certificate> it = expectedChain.iterator();
+ while (it.hasNext()) {
+ assertEquals("CA chain certificate should equal what we put in", it.next(),
+ actualChain[i++]);
+ }
+ }
+ }
+
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null",
+ mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NoPassword_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+ assertNotNull("Key should exist", key);
+
+ assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
+
+ RSAPrivateKey actualKey = (RSAPrivateKey) key;
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ assertEquals("Inserted key should be same as retrieved key", actualKey, expectedKey);
+ }
+
+ public void testKeyStore_GetKey_Certificate_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NonExistent_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetProvider_Success() throws Exception {
+ assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
+ }
+
+ public void testKeyStore_GetType_Success() throws Exception {
+ assertEquals(AndroidKeyStore.NAME, mKeyStore.getType());
+ }
+
+ public void testKeyStore_IsCertificateEntry_CA_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertTrue("Should return true for CA certificate",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsCertificateEntry_PrivateKey_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertFalse("Should return false for PrivateKeyEntry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsCertificateEntry_NonExist_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_PrivateKey_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_CA_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_NonExist_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_SetCertificate_CA_Success() throws Exception {
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+ final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual);
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+ assertEquals("Retrieved certificate should be the same as the one inserted", actual,
+ retrieved);
+ }
+
+ public void testKeyStore_SetCertificate_CAExists_Overwrite_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+ final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // TODO have separate FAKE_CA for second test
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+ }
+
+ public void testKeyStore_SetCertificate_PrivateKeyExists_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+ final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ try {
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+ fail("Should throw when trying to overwrite a PrivateKey entry with a Certificate");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Start with PrivateKeyEntry
+ {
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // TODO make entirely new test vector for the overwrite
+ // Replace with PrivateKeyEntry
+ {
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Start with TrustedCertificateEntry
+ {
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+
+ // Replace with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Start with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = caCert;
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // Replace with TrustedCertificateEntry
+ {
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Start with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = caCert;
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // Replace with PrivateKeyEntry that has no chain
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[1];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, null);
+ }
+ }
+
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Insert TrustedCertificateEntry
+ {
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+
+ // Replace with TrustedCertificateEntry of USER
+ {
+ final Certificate userCert = f
+ .generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ TrustedCertificateEntry expectedUserEntry = new TrustedCertificateEntry(userCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedUserEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualUserEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedUserEntry.getTrustedCertificate(),
+ actualUserEntry.getTrustedCertificate());
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_ProtectedKey_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ try {
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, "foo".toCharArray(), chain);
+ fail("Should fail when a password is specified");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetKeyEntry_Replaced_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Insert initial key
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // TODO make a separate key
+ // Replace key
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_Size_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+
+ assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+
+ assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+
+ assertEquals("The keystore size should match expected", 3, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ assertTrue(mAndroidKeyStore.delete(Credentials.CA_CERTIFICATE + TEST_ALIAS_1));
+
+ assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ assertTrue(mAndroidKeyStore.delKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+
+ assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_2 });
+ }
+
+ public void testKeyStore_Store_LoadStoreParam_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ try {
+ mKeyStore.store(null);
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+ }
+
+ public void testKeyStore_Load_InputStreamSupplied_Failure() throws Exception {
+ byte[] buf = "FAKE KEYSTORE".getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream(buf);
+
+ try {
+ mKeyStore.load(is, null);
+ fail("Should throw IllegalArgumentException when InputStream is supplied");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyStore_Load_PasswordSupplied_Failure() throws Exception {
+ try {
+ mKeyStore.load(null, "password".toCharArray());
+ fail("Should throw IllegalArgumentException when password is supplied");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyStore_Store_OutputStream_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ OutputStream sink = new ByteArrayOutputStream();
+ try {
+ mKeyStore.store(sink, null);
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+
+ try {
+ mKeyStore.store(sink, "blah".toCharArray());
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+ }
+}
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 9f35b8d..07a2d7b 100755
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -19,9 +19,11 @@
import android.app.Activity;
import android.security.KeyStore;
import android.test.ActivityUnitTestCase;
+import android.test.AssertionFailedError;
import android.test.suitebuilder.annotation.MediumTest;
import java.nio.charset.Charsets;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
/**
@@ -403,4 +405,52 @@
assertFalse("Should fail to ungrant key to other user second time",
mKeyStore.ungrant(TEST_KEYNAME, 0));
}
+
+ /**
+ * The amount of time to allow before and after expected time for variance
+ * in timing tests.
+ */
+ private static final long SLOP_TIME_MILLIS = 15000L;
+
+ public void testGetmtime_Success() throws Exception {
+ assertTrue("Password should work for keystore",
+ mKeyStore.password(TEST_PASSWD));
+
+ assertTrue("Should be able to import key when unlocked",
+ mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+
+ long now = System.currentTimeMillis();
+ long actual = mKeyStore.getmtime(TEST_KEYNAME);
+
+ long expectedAfter = now - SLOP_TIME_MILLIS;
+ long expectedBefore = now + SLOP_TIME_MILLIS;
+
+ assertLessThan("Time should be close to current time", expectedBefore, actual);
+ assertGreaterThan("Time should be close to current time", expectedAfter, actual);
+ }
+
+ private static void assertLessThan(String explanation, long expectedBefore, long actual) {
+ if (actual >= expectedBefore) {
+ throw new AssertionFailedError(explanation + ": actual=" + actual
+ + ", expected before: " + expectedBefore);
+ }
+ }
+
+ private static void assertGreaterThan(String explanation, long expectedAfter, long actual) {
+ if (actual <= expectedAfter) {
+ throw new AssertionFailedError(explanation + ": actual=" + actual
+ + ", expected after: " + expectedAfter);
+ }
+ }
+
+ public void testGetmtime_NonExist_Failure() throws Exception {
+ assertTrue("Password should work for keystore",
+ mKeyStore.password(TEST_PASSWD));
+
+ assertTrue("Should be able to import key when unlocked",
+ mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+
+ assertEquals("-1 should be returned for non-existent key",
+ -1L, mKeyStore.getmtime(TEST_KEYNAME2));
+ }
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 375ba68..cb6ce4b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2592,6 +2592,11 @@
// @see bug/4455071
handleConnectivityChange(info.getType(), false);
break;
+ case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+ info = (NetworkInfo) msg.obj;
+ type = info.getType();
+ updateNetworkSettings(mNetTrackers[type]);
+ break;
}
}
}