1f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root/*
2f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * Copyright 2014 The Android Open Source Project
3f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root *
4f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * you may not use this file except in compliance with the License.
6f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * You may obtain a copy of the License at
7f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root *
8f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root *
10f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * Unless required by applicable law or agreed to in writing, software
11f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * See the License for the specific language governing permissions and
14f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * limitations under the License.
15f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root */
16f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
17f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootpackage org.conscrypt;
18f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
19f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.NoSuchAlgorithmException;
20f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.PrivateKey;
21f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.Provider;
22f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.Security;
23f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.Signature;
24f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.interfaces.DSAPrivateKey;
25f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.interfaces.ECPrivateKey;
26f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport java.security.interfaces.RSAPrivateKey;
27f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport javax.crypto.Cipher;
28f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootimport javax.crypto.NoSuchPaddingException;
29f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
30f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root/**
31f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * Provides a place where NativeCrypto can call back up to do Java language
32f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root * calls to work on delegated key types from native code.
33f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root */
34f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Rootpublic final class CryptoUpcalls {
35f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    private static final String RSA_CRYPTO_ALGORITHM = "RSA/ECB/PKCS1Padding";
36f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
37f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    private CryptoUpcalls() {
38f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    }
39f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
40f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    /**
41f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root     * Finds the first provider which provides {@code algorithm} but is not from
42f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root     * the same ClassLoader as ours.
43f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root     */
44f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    public static Provider getExternalProvider(String algorithm) {
45f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        Provider selectedProvider = null;
46f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        for (Provider p : Security.getProviders(algorithm)) {
47f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            if (!p.getClass().getClassLoader().equals(CryptoUpcalls.class.getClassLoader())) {
48f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root                selectedProvider = p;
49f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root                break;
50f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            }
51f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
52f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (selectedProvider == null) {
53f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Could not find external provider for algorithm: " + algorithm);
54f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
55f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        return selectedProvider;
56f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    }
57f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
58f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    public static byte[] rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
59f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        // Get the raw signature algorithm for this key type.
60f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        String algorithm = null;
61f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        // Hint: Algorithm names come from:
62f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
63f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (javaKey instanceof RSAPrivateKey) {
64f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            // IMPORTANT: Due to a platform bug, this will throw
65f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            // NoSuchAlgorithmException
66f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
67f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            // See https://android-review.googlesource.com/#/c/40352/
68f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            algorithm = "NONEwithRSA";
69f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } else if (javaKey instanceof DSAPrivateKey) {
70f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            algorithm = "NONEwithDSA";
71f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } else if (javaKey instanceof ECPrivateKey) {
72f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            algorithm = "NONEwithECDSA";
73f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } else {
74f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            throw new RuntimeException("Unexpected key type: " + javaKey.toString());
75f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
76f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
77f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        Provider p = getExternalProvider("Signature." + algorithm);
78f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (p == null) {
79f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
80f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
81f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
82f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        // Get the Signature for this key.
83f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        Signature signature = null;
84f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        try {
85f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            signature = Signature.getInstance(algorithm, p);
86f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } catch (NoSuchAlgorithmException e) {
87f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            ;
88f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
89f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
90f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (signature == null) {
91f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Unsupported private key algorithm: " + javaKey.getAlgorithm());
92f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
93f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
94f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
95f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        // Sign the message.
96f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        try {
97f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            signature.initSign(javaKey);
98f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            signature.update(message);
99f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return signature.sign();
100f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } catch (Exception e) {
101f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Exception while signing message with " + javaKey.getAlgorithm()
102f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root                    + " private key:");
103f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            e.printStackTrace();
104f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
105f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
106f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    }
107f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
108f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    public static byte[] rawCipherWithPrivateKey(PrivateKey javaKey, boolean encrypt,
109f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            byte[] input) {
110f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (!(javaKey instanceof RSAPrivateKey)) {
111f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Unexpected key type: " + javaKey.toString());
112f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
113f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
114f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
115f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        Provider p = getExternalProvider("Cipher." + RSA_CRYPTO_ALGORITHM);
116f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (p == null) {
117f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
118f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
119f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
120f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        Cipher c = null;
121f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        try {
122f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            c = Cipher.getInstance(RSA_CRYPTO_ALGORITHM, p);
123f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } catch (NoSuchAlgorithmException e) {
124f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            ;
125f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } catch (NoSuchPaddingException e) {
126f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            ;
127f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
128f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
129f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        if (c == null) {
130f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Unsupported private key algorithm: " + javaKey.getAlgorithm());
131f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
132f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root
133f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        try {
134f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            c.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, javaKey);
135f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return c.doFinal(input);
136f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        } catch (Exception e) {
137f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            System.err.println("Exception while ciphering message with " + javaKey.getAlgorithm()
138f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root                    + " private key:");
139f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            e.printStackTrace();
140f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root            return null;
141f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root        }
142f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root    }
143f24ba0620d88b7d71ddb089b97d29fb1b073718dKenny Root}
144