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