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