18da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor/*
28da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * Copyright (C) 2010 The Android Open Source Project
38da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor *
48da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * Licensed under the Apache License, Version 2.0 (the "License");
58da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * you may not use this file except in compliance with the License.
68da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * You may obtain a copy of the License at
78da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor *
88da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor *      http://www.apache.org/licenses/LICENSE-2.0
98da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor *
108da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * Unless required by applicable law or agreed to in writing, software
118da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * distributed under the License is distributed on an "AS IS" BASIS,
128da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * See the License for the specific language governing permissions and
148da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor * limitations under the License.
158da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor */
168da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
178da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorpackage android.security;
188da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
198da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport android.os.Environment;
201ff8fee7c8e4fcd4ef12c6c5d1055b7eccf5809bOscar Montemayorimport android.os.FileUtils;
218da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport android.os.Process;
228da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
238da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.io.File;
248da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.io.FileInputStream;
258da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.io.FileOutputStream;
268da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.io.IOException;
278da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.security.NoSuchAlgorithmException;
288da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport java.security.SecureRandom;
298da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
308da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport javax.crypto.KeyGenerator;
318da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorimport javax.crypto.SecretKey;
328da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
33460c26e9875833dc494575b5c43f08c8baa15f34Elliott Hughesimport libcore.io.IoUtils;
34460c26e9875833dc494575b5c43f08c8baa15f34Elliott Hughes
358da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor/**
368da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor *@hide
378da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor */
388da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayorpublic class SystemKeyStore {
398da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
408da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    private static final String SYSTEM_KEYSTORE_DIRECTORY = "misc/systemkeys";
41b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    private static final String KEY_FILE_EXTENSION = ".sks";
428da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    private static SystemKeyStore mInstance = new SystemKeyStore();
438da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
448da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    private SystemKeyStore() { }
458da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
468da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    public static SystemKeyStore getInstance() {
478da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        return mInstance;
488da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    }
498da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
50b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    public static String toHexString(byte[] keyData) {
51b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        if (keyData == null) {
52b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            return null;
53b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        }
54b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        int keyLen = keyData.length;
55b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        int expectedStringLen = keyData.length * 2;
56b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        StringBuilder sb = new StringBuilder(expectedStringLen);
57b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        for (int i = 0; i < keyData.length; i++) {
58b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            String hexStr = Integer.toString(keyData[i] & 0x00FF, 16);
59b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            if (hexStr.length() == 1) {
60b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor                hexStr = "0" + hexStr;
61b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            }
62b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            sb.append(hexStr);
63b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        }
64b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        return sb.toString();
65b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    }
66b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor
67b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    public String generateNewKeyHexString(int numBits, String algName, String keyName)
68b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor            throws NoSuchAlgorithmException {
69b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        return toHexString(generateNewKey(numBits, algName, keyName));
70b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    }
71b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor
728da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    public byte[] generateNewKey(int numBits, String algName, String keyName)
738da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            throws NoSuchAlgorithmException {
748da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
758da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        // Check if key with similar name exists. If so, return null.
768da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        File keyFile = getKeyFile(keyName);
778da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        if (keyFile.exists()) {
788da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            throw new IllegalArgumentException();
798da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        }
808da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
818da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        KeyGenerator skg = KeyGenerator.getInstance(algName);
828da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        SecureRandom srng = SecureRandom.getInstance("SHA1PRNG");
838da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        skg.init(numBits, srng);
848da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
858da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        SecretKey sk = skg.generateKey();
868da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        byte[] retKey = sk.getEncoded();
878da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
888da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        try {
898da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            // Store the key
908da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            if (!keyFile.createNewFile()) {
918da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor                throw new IllegalArgumentException();
928da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            }
938da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
948da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            FileOutputStream fos = new FileOutputStream(keyFile);
958da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            fos.write(retKey);
968da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            fos.flush();
978bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            FileUtils.sync(fos);
988da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            fos.close();
991ff8fee7c8e4fcd4ef12c6c5d1055b7eccf5809bOscar Montemayor            FileUtils.setPermissions(keyFile.getName(), (FileUtils.S_IRUSR | FileUtils.S_IWUSR),
1001ff8fee7c8e4fcd4ef12c6c5d1055b7eccf5809bOscar Montemayor                -1, -1);
1018da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        } catch (IOException ioe) {
1028da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            return null;
1038da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        }
1048da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        return retKey;
1058da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    }
1068da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
1078da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    private File getKeyFile(String keyName) {
1088da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        File sysKeystoreDir = new File(Environment.getDataDirectory(),
1098da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor                SYSTEM_KEYSTORE_DIRECTORY);
110b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION);
1118da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        return keyFile;
1128da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    }
1138da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
1148d578836dc4f9fb41532b8b3dd7a6b168d6f4f9dRich Cannings    public String retrieveKeyHexString(String keyName) throws IOException {
115b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor        return toHexString(retrieveKey(keyName));
116b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor    }
117b62e8132df0d19a39a700324475b3df2de78e0b0Oscar Montemayor
1188d578836dc4f9fb41532b8b3dd7a6b168d6f4f9dRich Cannings    public byte[] retrieveKey(String keyName) throws IOException {
1198da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        File keyFile = getKeyFile(keyName);
1208da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        if (!keyFile.exists()) {
1218da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            return null;
1228da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        }
123460c26e9875833dc494575b5c43f08c8baa15f34Elliott Hughes        return IoUtils.readFileAsByteArray(keyFile.toString());
1248da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    }
1258da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
1268da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    public void deleteKey(String keyName) {
1278da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
1288da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        // Get the file first.
1298da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        File keyFile = getKeyFile(keyName);
1308da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        if (!keyFile.exists()) {
1318da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor            throw new IllegalArgumentException();
1328da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        }
1338da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor
1348da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor        keyFile.delete();
1358da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor    }
1368da98e30d8b2ae6e203f769dab0d6ec34cab3011Oscar Montemayor}
137