KeyStore.java revision 5b1f037829bff93877a6257db69f4e7723a27e20
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.security; 18 19import android.net.LocalSocketAddress; 20import android.net.LocalSocket; 21 22import java.io.InputStream; 23import java.io.IOException; 24import java.io.OutputStream; 25import java.io.UTFDataFormatException; 26import java.nio.charset.Charsets; 27import java.nio.charset.ModifiedUtf8; 28import java.util.ArrayList; 29 30/** 31 * @hide This should not be made public in its present form because it 32 * assumes that private and secret key bytes are available and would 33 * preclude the use of hardware crypto. 34 */ 35public class KeyStore { 36 37 // ResponseCodes 38 public static final int NO_ERROR = 1; 39 public static final int LOCKED = 2; 40 public static final int UNINITIALIZED = 3; 41 public static final int SYSTEM_ERROR = 4; 42 public static final int PROTOCOL_ERROR = 5; 43 public static final int PERMISSION_DENIED = 6; 44 public static final int KEY_NOT_FOUND = 7; 45 public static final int VALUE_CORRUPTED = 8; 46 public static final int UNDEFINED_ACTION = 9; 47 public static final int WRONG_PASSWORD = 10; 48 49 // States 50 public enum State { UNLOCKED, LOCKED, UNINITIALIZED }; 51 52 private static final LocalSocketAddress sAddress = new LocalSocketAddress( 53 "keystore", LocalSocketAddress.Namespace.RESERVED); 54 55 private int mError = NO_ERROR; 56 57 private KeyStore() {} 58 59 public static KeyStore getInstance() { 60 return new KeyStore(); 61 } 62 63 public State state() { 64 execute('t'); 65 switch (mError) { 66 case NO_ERROR: return State.UNLOCKED; 67 case LOCKED: return State.LOCKED; 68 case UNINITIALIZED: return State.UNINITIALIZED; 69 default: throw new AssertionError(mError); 70 } 71 } 72 73 private byte[] get(byte[] key) { 74 ArrayList<byte[]> values = execute('g', key); 75 return (values == null || values.isEmpty()) ? null : values.get(0); 76 } 77 78 public byte[] get(String key) { 79 return get(getKeyBytes(key)); 80 } 81 82 private boolean put(byte[] key, byte[] value) { 83 execute('i', key, value); 84 return mError == NO_ERROR; 85 } 86 87 public boolean put(String key, byte[] value) { 88 return put(getKeyBytes(key), value); 89 } 90 91 private boolean delete(byte[] key) { 92 execute('d', key); 93 return mError == NO_ERROR; 94 } 95 96 public boolean delete(String key) { 97 return delete(getKeyBytes(key)); 98 } 99 100 private boolean contains(byte[] key) { 101 execute('e', key); 102 return mError == NO_ERROR; 103 } 104 105 public boolean contains(String key) { 106 return contains(getKeyBytes(key)); 107 } 108 109 public byte[][] saw(byte[] prefix) { 110 ArrayList<byte[]> values = execute('s', prefix); 111 return (values == null) ? null : values.toArray(new byte[values.size()][]); 112 } 113 114 public String[] saw(String prefix) { 115 byte[][] values = saw(getKeyBytes(prefix)); 116 if (values == null) { 117 return null; 118 } 119 String[] strings = new String[values.length]; 120 for (int i = 0; i < values.length; ++i) { 121 strings[i] = toKeyString(values[i]); 122 } 123 return strings; 124 } 125 126 public boolean reset() { 127 execute('r'); 128 return mError == NO_ERROR; 129 } 130 131 private boolean password(byte[] password) { 132 execute('p', password); 133 return mError == NO_ERROR; 134 } 135 136 public boolean password(String password) { 137 return password(getPasswordBytes(password)); 138 } 139 140 public boolean lock() { 141 execute('l'); 142 return mError == NO_ERROR; 143 } 144 145 private boolean unlock(byte[] password) { 146 execute('u', password); 147 return mError == NO_ERROR; 148 } 149 150 public boolean unlock(String password) { 151 return unlock(getPasswordBytes(password)); 152 } 153 154 public boolean isEmpty() { 155 execute('z'); 156 return mError == KEY_NOT_FOUND; 157 } 158 159 private boolean generate(byte[] key) { 160 execute('a', key); 161 return mError == NO_ERROR; 162 } 163 164 public boolean generate(String key) { 165 return generate(getKeyBytes(key)); 166 } 167 168 private boolean importKey(byte[] keyName, byte[] key) { 169 execute('m', keyName, key); 170 return mError == NO_ERROR; 171 } 172 173 public boolean importKey(String keyName, byte[] key) { 174 return importKey(getKeyBytes(keyName), key); 175 } 176 177 private byte[] getPubkey(byte[] key) { 178 ArrayList<byte[]> values = execute('b', key); 179 return (values == null || values.isEmpty()) ? null : values.get(0); 180 } 181 182 public byte[] getPubkey(String key) { 183 return getPubkey(getKeyBytes(key)); 184 } 185 186 private boolean delKey(byte[] key) { 187 execute('k', key); 188 return mError == NO_ERROR; 189 } 190 191 public boolean delKey(String key) { 192 return delKey(getKeyBytes(key)); 193 } 194 195 private byte[] sign(byte[] keyName, byte[] data) { 196 final ArrayList<byte[]> values = execute('n', keyName, data); 197 return (values == null || values.isEmpty()) ? null : values.get(0); 198 } 199 200 public byte[] sign(String key, byte[] data) { 201 return sign(getKeyBytes(key), data); 202 } 203 204 private boolean verify(byte[] keyName, byte[] data, byte[] signature) { 205 execute('v', keyName, data, signature); 206 return mError == NO_ERROR; 207 } 208 209 public boolean verify(String key, byte[] data, byte[] signature) { 210 return verify(getKeyBytes(key), data, signature); 211 } 212 213 private boolean grant(byte[] key, byte[] uid) { 214 execute('x', key, uid); 215 return mError == NO_ERROR; 216 } 217 218 public boolean grant(String key, int uid) { 219 return grant(getKeyBytes(key), getUidBytes(uid)); 220 } 221 222 private boolean ungrant(byte[] key, byte[] uid) { 223 execute('y', key, uid); 224 return mError == NO_ERROR; 225 } 226 227 public boolean ungrant(String key, int uid) { 228 return ungrant(getKeyBytes(key), getUidBytes(uid)); 229 } 230 231 public int getLastError() { 232 return mError; 233 } 234 235 private ArrayList<byte[]> execute(int code, byte[]... parameters) { 236 mError = PROTOCOL_ERROR; 237 238 for (byte[] parameter : parameters) { 239 if (parameter == null || parameter.length > 65535) { 240 return null; 241 } 242 } 243 244 LocalSocket socket = new LocalSocket(); 245 try { 246 socket.connect(sAddress); 247 248 OutputStream out = socket.getOutputStream(); 249 out.write(code); 250 for (byte[] parameter : parameters) { 251 out.write(parameter.length >> 8); 252 out.write(parameter.length); 253 out.write(parameter); 254 } 255 out.flush(); 256 socket.shutdownOutput(); 257 258 InputStream in = socket.getInputStream(); 259 if ((code = in.read()) != NO_ERROR) { 260 if (code != -1) { 261 mError = code; 262 } 263 return null; 264 } 265 266 ArrayList<byte[]> values = new ArrayList<byte[]>(); 267 while (true) { 268 int i, j; 269 if ((i = in.read()) == -1) { 270 break; 271 } 272 if ((j = in.read()) == -1) { 273 return null; 274 } 275 byte[] value = new byte[i << 8 | j]; 276 for (i = 0; i < value.length; i += j) { 277 if ((j = in.read(value, i, value.length - i)) == -1) { 278 return null; 279 } 280 } 281 values.add(value); 282 } 283 mError = NO_ERROR; 284 return values; 285 } catch (IOException e) { 286 // ignore 287 } finally { 288 try { 289 socket.close(); 290 } catch (IOException e) {} 291 } 292 return null; 293 } 294 295 /** 296 * ModifiedUtf8 is used for key encoding to match the 297 * implementation of NativeCrypto.ENGINE_load_private_key. 298 */ 299 private static byte[] getKeyBytes(String string) { 300 try { 301 int utfCount = (int) ModifiedUtf8.countBytes(string, false); 302 byte[] result = new byte[utfCount]; 303 ModifiedUtf8.encode(result, 0, string); 304 return result; 305 } catch (UTFDataFormatException e) { 306 throw new RuntimeException(e); 307 } 308 } 309 310 private static String toKeyString(byte[] bytes) { 311 try { 312 return ModifiedUtf8.decode(bytes, new char[bytes.length], 0, bytes.length); 313 } catch (UTFDataFormatException e) { 314 throw new RuntimeException(e); 315 } 316 } 317 318 private static byte[] getPasswordBytes(String password) { 319 return password.getBytes(Charsets.UTF_8); 320 } 321 322 private static byte[] getUidBytes(int uid) { 323 return Integer.toString(uid).getBytes(Charsets.UTF_8); 324 } 325} 326