1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import org.chromium.base.Log;
8 import org.chromium.base.annotations.CalledByNative;
9 import org.chromium.base.annotations.JNINamespace;
10 
11 import java.security.InvalidKeyException;
12 import java.security.NoSuchAlgorithmException;
13 import java.security.PrivateKey;
14 import java.security.Signature;
15 
16 import javax.crypto.Cipher;
17 import javax.crypto.NoSuchPaddingException;
18 
19 /**
20  * Specifies all the dependencies from the native OpenSSL engine on an Android KeyStore.
21  */
22 @JNINamespace("net::android")
23 public class AndroidKeyStore {
24     private static final String TAG = "AndroidKeyStore";
25 
26     @CalledByNative
getPrivateKeyClassName(PrivateKey privateKey)27     private static String getPrivateKeyClassName(PrivateKey privateKey) {
28         return privateKey.getClass().getName();
29     }
30 
31     /**
32      * Check if a given PrivateKey object supports a signature algorithm.
33      *
34      * @param privateKey The PrivateKey handle.
35      * @param algorithm The signature algorithm to use.
36      * @return whether the algorithm is supported.
37      */
38     @CalledByNative
privateKeySupportsSignature(PrivateKey privateKey, String algorithm)39     private static boolean privateKeySupportsSignature(PrivateKey privateKey, String algorithm) {
40         try {
41             Signature signature = Signature.getInstance(algorithm);
42             signature.initSign(privateKey);
43         } catch (NoSuchAlgorithmException | InvalidKeyException e) {
44             return false;
45         } catch (Exception e) {
46             Log.e(TAG, "Exception while checking support for " + algorithm + ": " + e);
47             return false;
48         }
49         return true;
50     }
51 
52     /**
53      * Check if a given PrivateKey object supports an encryption algorithm.
54      *
55      * @param privateKey The PrivateKey handle.
56      * @param algorithm The signature algorithm to use.
57      * @return whether the algorithm is supported.
58      */
59     @CalledByNative
privateKeySupportsCipher(PrivateKey privateKey, String algorithm)60     private static boolean privateKeySupportsCipher(PrivateKey privateKey, String algorithm) {
61         try {
62             Cipher cipher = Cipher.getInstance(algorithm);
63             cipher.init(Cipher.ENCRYPT_MODE, privateKey);
64         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
65             return false;
66         } catch (Exception e) {
67             Log.e(TAG, "Exception while checking support for " + algorithm + ": " + e);
68             return false;
69         }
70         return true;
71     }
72 
73     /**
74      * Sign a given message with a given PrivateKey object.
75      *
76      * @param privateKey The PrivateKey handle.
77      * @param algorithm The signature algorithm to use.
78      * @param message The message to sign.
79      * @return signature as a byte buffer.
80      *
81      * Note: NONEwithRSA is not implemented in Android < 4.2. See
82      * getOpenSSLHandleForPrivateKey() below for a work-around.
83      */
84     @CalledByNative
signWithPrivateKey( PrivateKey privateKey, String algorithm, byte[] message)85     private static byte[] signWithPrivateKey(
86             PrivateKey privateKey, String algorithm, byte[] message) {
87         // Hint: Algorithm names come from:
88         // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
89         Signature signature = null;
90         try {
91             signature = Signature.getInstance(algorithm);
92         } catch (NoSuchAlgorithmException e) {
93             Log.e(TAG, "Signature algorithm " + algorithm + " not supported: " + e);
94             return null;
95         }
96 
97         try {
98             signature.initSign(privateKey);
99             signature.update(message);
100             return signature.sign();
101         } catch (Exception e) {
102             Log.e(TAG,
103                     "Exception while signing message with " + algorithm + " and "
104                             + privateKey.getAlgorithm() + " private key ("
105                             + privateKey.getClass().getName() + "): " + e);
106             return null;
107         }
108     }
109 
110     /**
111      * Encrypts a given input with a given PrivateKey object.
112      *
113      * @param privateKey The PrivateKey handle.
114      * @param algorithm The cipher to use.
115      * @param input The input to encrypt.
116      * @return ciphertext as a byte buffer.
117      *
118      * Note: NONEwithRSA is not implemented in Android < 4.2. See
119      * getOpenSSLHandleForPrivateKey() below for a work-around.
120      */
121     @CalledByNative
encryptWithPrivateKey( PrivateKey privateKey, String algorithm, byte[] message)122     private static byte[] encryptWithPrivateKey(
123             PrivateKey privateKey, String algorithm, byte[] message) {
124         Cipher cipher = null;
125         try {
126             cipher = Cipher.getInstance(algorithm);
127         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
128             Log.e(TAG, "Cipher " + algorithm + " not supported: " + e);
129             return null;
130         }
131 
132         try {
133             cipher.init(Cipher.ENCRYPT_MODE, privateKey);
134             return cipher.doFinal(message);
135         } catch (Exception e) {
136             Log.e(TAG,
137                     "Exception while encrypting input with " + algorithm + " and "
138                             + privateKey.getAlgorithm() + " private key ("
139                             + privateKey.getClass().getName() + "): " + e);
140             return null;
141         }
142     }
143 }
144