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