• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 
17 package org.conscrypt;
18 
19 import java.security.InvalidKeyException;
20 import java.security.NoSuchAlgorithmException;
21 import java.security.PrivateKey;
22 import java.security.Provider;
23 import java.security.Security;
24 import java.security.Signature;
25 import java.util.ArrayList;
26 import javax.crypto.Cipher;
27 import javax.crypto.NoSuchPaddingException;
28 
29 /**
30  * Provides a place where NativeCrypto can call back up to do Java language
31  * calls to work on delegated key types from native code. Delegated keys are
32  * usually backed by hardware so we don't have access directly to the private
33  * key material. If it were a key where we can get to the private key, we
34  * would not ever call into this class.
35  */
36 final class CryptoUpcalls {
37 
CryptoUpcalls()38     private CryptoUpcalls() {
39     }
40 
isOurProvider(Provider p)41     private static boolean isOurProvider(Provider p) {
42         return p.getClass().getPackage().equals(CryptoUpcalls.class.getPackage());
43     }
44 
45     /**
46      * Finds providers that are not us that provide the requested algorithms.
47      */
getExternalProviders(String algorithm)48     private static ArrayList<Provider> getExternalProviders(String algorithm) {
49         ArrayList<Provider> providers = new ArrayList<>(1);
50         for (Provider p : Security.getProviders(algorithm)) {
51             if (!isOurProvider(p)) {
52                 providers.add(p);
53             }
54         }
55         if (providers.isEmpty()) {
56             System.err.println("Could not find external provider for algorithm: " + algorithm);
57         }
58         return providers;
59     }
60 
rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message)61     static byte[] rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
62         // Get the raw signature algorithm for this key type.
63         String algorithm;
64         // Hint: Algorithm names come from:
65         // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
66         String keyAlgorithm = javaKey.getAlgorithm();
67         if ("RSA".equals(keyAlgorithm)) {
68             // IMPORTANT: Due to a platform bug, this will throw
69             // NoSuchAlgorithmException
70             // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
71             // See https://android-review.googlesource.com/#/c/40352/
72             algorithm = "NONEwithRSA";
73         } else if ("EC".equals(keyAlgorithm)) {
74             algorithm = "NONEwithECDSA";
75         } else {
76             throw new RuntimeException("Unexpected key type: " + javaKey.toString());
77         }
78 
79         Signature signature;
80 
81         // Since this is a delegated key, we cannot handle providing a signature using this key.
82         // Otherwise we wouldn't end up in this classs in the first place. The first step is to
83         // try to get the most preferred provider as long as it isn't us.
84         try {
85             signature = Signature.getInstance(algorithm);
86             signature.initSign(javaKey);
87 
88             // Ignore it if it points back to us.
89             if (isOurProvider(signature.getProvider())) {
90                 signature = null;
91             }
92         } catch (NoSuchAlgorithmException e) {
93             System.err.println("Unsupported signature algorithm: " + algorithm);
94             return null;
95         } catch (InvalidKeyException e) {
96             System.err.println("Preferred provider doesn't support key:");
97             e.printStackTrace();
98             signature = null;
99         }
100 
101         // If the preferred provider was us, fall back to trying to find the
102         // first not-us provider that initializes correctly.
103         if (signature == null) {
104             ArrayList<Provider> providers = getExternalProviders("Signature." + algorithm);
105             for (Provider p : providers) {
106                 try {
107                     signature = Signature.getInstance(algorithm, p);
108                     signature.initSign(javaKey);
109                     break;
110                 } catch (NoSuchAlgorithmException | InvalidKeyException e) {
111                     signature = null;
112                 }
113             }
114             if (signature == null) {
115                 System.err.println("Could not find provider for algorithm: " + algorithm);
116                 return null;
117             }
118         }
119 
120         // Sign the message.
121         try {
122             signature.update(message);
123             return signature.sign();
124         } catch (Exception e) {
125             System.err.println("Exception while signing message with " + javaKey.getAlgorithm()
126                     + " private key:");
127             e.printStackTrace();
128             return null;
129         }
130     }
131 
rsaDecryptWithPrivateKey(PrivateKey javaKey, int openSSLPadding, byte[] input)132     static byte[] rsaDecryptWithPrivateKey(PrivateKey javaKey, int openSSLPadding,
133             byte[] input) {
134         String keyAlgorithm = javaKey.getAlgorithm();
135         if (!"RSA".equals(keyAlgorithm)) {
136             System.err.println("Unexpected key type: " + keyAlgorithm);
137             return null;
138         }
139 
140         String jcaPadding;
141         switch (openSSLPadding) {
142             case NativeConstants.RSA_PKCS1_PADDING:
143                 jcaPadding = "PKCS1Padding";
144                 break;
145             case NativeConstants.RSA_NO_PADDING:
146                 jcaPadding = "NoPadding";
147                 break;
148             case NativeConstants.RSA_PKCS1_OAEP_PADDING:
149                 jcaPadding = "OAEPPadding";
150                 break;
151             default:
152                 System.err.println("Unsupported OpenSSL/BoringSSL padding: " + openSSLPadding);
153                 return null;
154         }
155 
156         String transformation = "RSA/ECB/" + jcaPadding;
157         Cipher c = null;
158 
159         // Since this is a delegated key, we cannot handle providing a cipher using this key.
160         // Otherwise we wouldn't end up in this classs in the first place. The first step is to
161         // try to get the most preferred provider as long as it isn't us.
162         try {
163             c = Cipher.getInstance(transformation);
164             c.init(Cipher.DECRYPT_MODE, javaKey);
165 
166             // Ignore it if it points back to us.
167             if (isOurProvider(c.getProvider())) {
168                 c = null;
169             }
170         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
171             System.err.println("Unsupported cipher algorithm: " + transformation);
172             return null;
173         } catch (InvalidKeyException e) {
174             System.err.println("Preferred provider doesn't support key:");
175             e.printStackTrace();
176             c = null;
177         }
178 
179         // If the preferred provider was us, fall back to trying to find the
180         // first not-us provider that initializes correctly.
181         if (c == null) {
182             ArrayList<Provider> providers = getExternalProviders("Cipher." + transformation);
183             for (Provider p : providers) {
184                 try {
185                     c = Cipher.getInstance(transformation, p);
186                     c.init(Cipher.DECRYPT_MODE, javaKey);
187                     break;
188                 } catch (NoSuchAlgorithmException | InvalidKeyException
189                         | NoSuchPaddingException e) {
190                     c = null;
191                 }
192             }
193             if (c == null) {
194                 System.err.println("Could not find provider for algorithm: " + transformation);
195                 return null;
196             }
197         }
198 
199         try {
200             return c.doFinal(input);
201         } catch (Exception e) {
202             System.err.println("Exception while decrypting message with " + javaKey.getAlgorithm()
203                     + " private key using " + transformation + ":");
204             e.printStackTrace();
205             return null;
206         }
207     }
208 }
209