NativeCrypto.java revision f002bdddce924e2145a4a2b60592b7a40f4112f6
1/* 2 * Copyright (C) 2008 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.apache.harmony.xnet.provider.jsse; 18 19import java.io.ByteArrayOutputStream; 20import java.io.IOException; 21import java.io.OutputStreamWriter; 22import java.net.Socket; 23import java.security.PrivateKey; 24import java.security.cert.CertificateException; 25import java.security.cert.X509Certificate; 26import java.util.ArrayList; 27 28import org.bouncycastle.openssl.PEMWriter; 29 30/** 31 * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing 32 * and verifying are covered. Is expected to grow over time. Also needs to move 33 * into libcore/openssl at some point. 34 */ 35public class NativeCrypto { 36 37 // --- OpenSSL library initialization -------------------------------------- 38 static { 39 clinit(); 40 } 41 42 private native static void clinit(); 43 44 // --- DSA/RSA public/private key handling functions ----------------------- 45 46 public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key); 47 48 public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q); 49 50 public static native void EVP_PKEY_free(int pkey); 51 52 // --- General context handling functions (despite the names) -------------- 53 54 public static native int EVP_new(); 55 56 public static native void EVP_free(int ctx); 57 58 // --- Digest handling functions ------------------------------------------- 59 60 public static native void EVP_DigestInit(int ctx, String algorithm); 61 62 public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length); 63 64 public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset); 65 66 public static native int EVP_DigestSize(int ctx); 67 68 public static native int EVP_DigestBlockSize(int ctx); 69 70 // --- Signature handling functions ---------------------------------------- 71 72 public static native void EVP_VerifyInit(int ctx, String algorithm); 73 74 public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length); 75 76 public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key); 77 78 // --- SSL handling -------------------------------------------------------- 79 80 private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3"; 81 private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1"; 82 83 // SSL mode 84 public static long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L; 85 86 // SSL options 87 public static long SSL_OP_NO_SSLv3 = 0x02000000L; 88 public static long SSL_OP_NO_TLSv1 = 0x04000000L; 89 90 public static native int SSL_CTX_new(); 91 92 public static native String[] SSL_CTX_get_ciphers(int ssl_ctx); 93 94 public static String[] getDefaultCipherSuites() { 95 int ssl_ctx = SSL_CTX_new(); 96 String[] supportedCiphers = SSL_CTX_get_ciphers(ssl_ctx); 97 SSL_CTX_free(ssl_ctx); 98 return supportedCiphers; 99 } 100 101 public static String[] getSupportedCipherSuites() { 102 // TODO really return full cipher list 103 return getDefaultCipherSuites(); 104 } 105 106 public static native void SSL_CTX_free(int ssl_ctx); 107 108 public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException; 109 110 /** 111 * Initialize the SSL socket and set the certificates for the 112 * future handshaking. 113 */ 114 public static int SSL_new(SSLParameters sslParameters) throws IOException { 115 boolean client = sslParameters.getUseClientMode(); 116 117 final int ssl_ctx = (client) ? 118 sslParameters.getClientSessionContext().sslCtxNativePointer : 119 sslParameters.getServerSessionContext().sslCtxNativePointer; 120 121 // TODO support more than RSA certificates? non-openssl 122 // SSLEngine implementation did these callbacks during 123 // handshake after selecting cipher suite, not before 124 // handshake. Should do the same via SSL_CTX_set_client_cert_cb 125 final String alias = (client) ? 126 sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null) : 127 sslParameters.getKeyManager().chooseServerAlias("RSA", null, null); 128 129 final String privateKeyString; 130 final String certificateString; 131 if (alias == null) { 132 privateKeyString = null; 133 certificateString = null; 134 } else { 135 PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); 136 X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); 137 138 ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream(); 139 PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS)); 140 privateKeyPEMWriter.writeObject(privateKey); 141 privateKeyPEMWriter.close(); 142 privateKeyString = privateKeyOS.toString(); 143 144 ByteArrayOutputStream certificateOS = new ByteArrayOutputStream(); 145 PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS)); 146 147 for (X509Certificate certificate : certificates) { 148 certificateWriter.writeObject(certificate); 149 } 150 certificateWriter.close(); 151 certificateString = certificateOS.toString(); 152 } 153 154 final byte[] seed = (sslParameters.getSecureRandomMember() != null) ? 155 sslParameters.getSecureRandomMember().generateSeed(1024) : 156 null; 157 158 return SSL_new(ssl_ctx, 159 privateKeyString, 160 certificateString, 161 seed); 162 } 163 164 165 public static native long SSL_get_mode(int ssl); 166 167 public static native long SSL_set_mode(int ssl, long mode); 168 169 public static native long SSL_clear_mode(int ssl, long mode); 170 171 public static native long SSL_get_options(int ssl); 172 173 public static native long SSL_set_options(int ssl, long options); 174 175 public static native long SSL_clear_options(int ssl, long options); 176 177 public static String[] getSupportedProtocols() { 178 return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 }; 179 } 180 181 public static String[] getEnabledProtocols(int ssl) { 182 long options = SSL_get_options(ssl); 183 ArrayList<String> array = new ArrayList<String>(); 184 if ((options & NativeCrypto.SSL_OP_NO_SSLv3) == 0) { 185 array.add(SUPPORTED_PROTOCOL_SSLV3); 186 } 187 if ((options & NativeCrypto.SSL_OP_NO_TLSv1) == 0) { 188 array.add(SUPPORTED_PROTOCOL_TLSV1); 189 } 190 return array.toArray(new String[array.size()]); 191 } 192 193 public static void setEnabledProtocols(int ssl, String[] protocols) { 194 if (protocols == null) { 195 throw new IllegalArgumentException("protocols == null"); 196 } 197 198 // openssl uses negative logic letting you disable protocols. 199 // so first, assume we need to set all (disable all ) and clear none (enable none). 200 // in the loop, selectively move bits from set to clear (from disable to enable) 201 long optionsToSet = (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); 202 long optionsToClear = 0; 203 for (int i = 0; i < protocols.length; i++) { 204 String protocol = protocols[i]; 205 if (protocol == null) { 206 throw new IllegalArgumentException("protocols[" + i + "] == null"); 207 } 208 if (protocol.equals(SUPPORTED_PROTOCOL_SSLV3)) { 209 optionsToSet &= ~SSL_OP_NO_SSLv3; 210 optionsToClear |= SSL_OP_NO_SSLv3; 211 } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) { 212 optionsToSet &= ~SSL_OP_NO_TLSv1; 213 optionsToClear |= SSL_OP_NO_TLSv1; 214 } else { 215 throw new IllegalArgumentException("Protocol " + protocol + 216 " is not supported"); 217 } 218 } 219 220 SSL_set_options(ssl, optionsToSet); 221 SSL_clear_options(ssl, optionsToClear); 222 } 223 224 public static String[] checkEnabledProtocols(String[] protocols) { 225 if (protocols == null) { 226 throw new IllegalArgumentException("protocols parameter is null"); 227 } 228 for (int i = 0; i < protocols.length; i++) { 229 String protocol = protocols[i]; 230 if (protocol == null) { 231 throw new IllegalArgumentException("protocols[" + i + "] == null"); 232 } 233 if ((!protocol.equals(SUPPORTED_PROTOCOL_SSLV3)) 234 && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1))) { 235 throw new IllegalArgumentException("Protocol " + protocol + 236 " is not supported"); 237 } 238 } 239 return protocols; 240 } 241 242 public static native String[] SSL_get_ciphers(int ssl); 243 244 public static native void SSL_set_cipher_list(int ssl, String ciphers); 245 246 public static void setEnabledCipherSuites(int ssl, String[] cipherSuites) { 247 checkEnabledCipherSuites(cipherSuites); 248 String controlString = ""; 249 for (int i = 0; i < cipherSuites.length; i++) { 250 String cipherSuite = cipherSuites[i]; 251 if (i == 0) { 252 controlString = cipherSuite; 253 } else { 254 controlString += ":" + cipherSuite; 255 } 256 } 257 SSL_set_cipher_list(ssl, controlString); 258 } 259 260 public static String[] checkEnabledCipherSuites(String[] cipherSuites) { 261 if (cipherSuites == null) { 262 throw new IllegalArgumentException("cipherSuites == null"); 263 } 264 // makes sure all suites are valid, throwing on error 265 String[] supportedCipherSuites = getSupportedCipherSuites(); 266 for (int i = 0; i < cipherSuites.length; i++) { 267 String cipherSuite = cipherSuites[i]; 268 if (cipherSuite == null) { 269 throw new IllegalArgumentException("cipherSuites[" + i + "] == null"); 270 } 271 findSuite(supportedCipherSuites, cipherSuite); 272 } 273 return cipherSuites; 274 } 275 276 private static void findSuite(String[] supportedCipherSuites, String suite) { 277 for (String supportedCipherSuite : supportedCipherSuites) { 278 if (supportedCipherSuite.equals(suite)) { 279 return; 280 } 281 } 282 throw new IllegalArgumentException("Protocol " + suite + " is not supported."); 283 } 284 285 /* 286 * See the OpenSSL ssl.h header file for more information. 287 */ 288 public static final int SSL_VERIFY_NONE = 0x00; 289 public static final int SSL_VERIFY_PEER = 0x01; 290 public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02; 291 public static final int SSL_VERIFY_CLIENT_ONCE = 0x04; 292 293 public static native void SSL_set_verify(int sslNativePointer, int mode) throws IOException; 294 295 public static native void SSL_set_session(int sslNativePointer, int sslSessionNativePointer) throws IOException; 296 297 public static native void SSL_set_session_creation_enabled(int sslNativePointer, boolean creationEnabled) throws IOException; 298 299 /** 300 * Returns the sslSessionNativePointer of the negotiated session 301 */ 302 public static native int SSL_do_handshake(int sslNativePointer, Socket sock, 303 CertificateChainVerifier ccv, HandshakeCompletedCallback hcc, 304 int timeout, boolean client_mode) throws IOException, CertificateException; 305 306 public static native byte[][] SSL_get_certificate(int sslNativePointer); 307 308 public static native void SSL_free(int sslNativePointer); 309 310 public static native byte[] SSL_SESSION_session_id(int sslSessionNativePointer); 311 312 /** 313 * Returns the X509 certificates of the peer in the PEM format. 314 */ 315 public static native byte[][] SSL_SESSION_get_peer_cert_chain(int sslCtxNativePointer, 316 int sslSessionNativePointer); 317 318 public static native long SSL_SESSION_get_time(int sslSessionNativePointer); 319 320 public static native String SSL_SESSION_get_version(int sslSessionNativePointer); 321 322 public static native String SSL_SESSION_cipher(int sslSessionNativePointer); 323 324 public static native void SSL_SESSION_free(int sslSessionNativePointer); 325 326 public static native byte[] i2d_SSL_SESSION(int sslSessionNativePointer); 327 328 public static native int d2i_SSL_SESSION(byte[] data, int size); 329 330 public interface CertificateChainVerifier { 331 /** 332 * Verify that we trust the certificate chain is trusted. 333 * 334 * @param bytes An array of certficates in PEM encode bytes 335 * @param authMethod auth algorithm name 336 * 337 * @throws CertificateException if the certificate is untrusted 338 */ 339 public void verifyCertificateChain(byte[][] bytes, String authMethod) throws CertificateException; 340 } 341 342 public interface HandshakeCompletedCallback { 343 /** 344 * Called when SSL handshake is completed. Note that this can 345 * be after SSL_do_handshake returns when handshake cutthrough 346 * is enabled. 347 */ 348 public void handshakeCompleted(); 349 } 350} 351