1 /*
2  * Copyright (C) 2012 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 javax.crypto.SecretKey;
23 
24 public class OpenSSLEngine {
25     static {
26         if (!NativeCrypto.isBoringSSL) {
NativeCrypto.ENGINE_load_dynamic()27             NativeCrypto.ENGINE_load_dynamic();
28         }
29     }
30 
31     private static final Object mLoadingLock = new Object();
32 
33     /** The ENGINE's native handle. */
34     private final long ctx;
35 
36     /**
37      * BoringSSL doesn't really use ENGINE objects, so we just keep this single
38      * instance around to satisfy API calls.
39      */
40     private static class BoringSSL {
41         public static final OpenSSLEngine INSTANCE = new OpenSSLEngine();
42     }
43 
getInstance(String engine)44     public static OpenSSLEngine getInstance(String engine) throws IllegalArgumentException {
45         if (NativeCrypto.isBoringSSL) {
46             return BoringSSL.INSTANCE;
47         }
48 
49         if (engine == null) {
50             throw new NullPointerException("engine == null");
51         }
52 
53         final long engineCtx;
54         synchronized (mLoadingLock) {
55             engineCtx = NativeCrypto.ENGINE_by_id(engine);
56             if (engineCtx == 0) {
57                 throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
58             }
59 
60             NativeCrypto.ENGINE_add(engineCtx);
61         }
62 
63         return new OpenSSLEngine(engineCtx);
64     }
65 
66     /**
67      * Used for BoringSSL. It doesn't use ENGINEs so there is no native pointer
68      * to keep track of.
69      */
OpenSSLEngine()70     private OpenSSLEngine() {
71         ctx = 0L;
72     }
73 
74     /**
75      * Used when OpenSSL is in use. It uses an ENGINE instance so we need to
76      * keep track if the native pointer for later freeing.
77      *
78      * @param engineCtx the ENGINE's native handle
79      */
OpenSSLEngine(long engineCtx)80     private OpenSSLEngine(long engineCtx) {
81         ctx = engineCtx;
82 
83         if (NativeCrypto.ENGINE_init(engineCtx) == 0) {
84             NativeCrypto.ENGINE_free(engineCtx);
85             throw new IllegalArgumentException("Could not initialize engine");
86         }
87     }
88 
getPrivateKeyById(String id)89     public PrivateKey getPrivateKeyById(String id) throws InvalidKeyException {
90         if (id == null) {
91             throw new NullPointerException("id == null");
92         }
93 
94         final long keyRef = NativeCrypto.ENGINE_load_private_key(ctx, id);
95         if (keyRef == 0) {
96             return null;
97         }
98 
99         OpenSSLKey pkey = new OpenSSLKey(keyRef, this, id);
100         try {
101             return pkey.getPrivateKey();
102         } catch (NoSuchAlgorithmException e) {
103             throw new InvalidKeyException(e);
104         }
105     }
106 
getSecretKeyById(String id, String algorithm)107     public SecretKey getSecretKeyById(String id, String algorithm) throws InvalidKeyException {
108         if (id == null) {
109             throw new NullPointerException("id == null");
110         }
111 
112         final long keyRef = NativeCrypto.ENGINE_load_private_key(ctx, id);
113         if (keyRef == 0) {
114             return null;
115         }
116 
117         OpenSSLKey pkey = new OpenSSLKey(keyRef, this, id);
118         try {
119             return pkey.getSecretKey(algorithm);
120         } catch (NoSuchAlgorithmException e) {
121             throw new InvalidKeyException(e);
122         }
123     }
124 
getEngineContext()125     long getEngineContext() {
126         return ctx;
127     }
128 
129     @Override
finalize()130     protected void finalize() throws Throwable {
131         try {
132             if (!NativeCrypto.isBoringSSL) {
133                 NativeCrypto.ENGINE_finish(ctx);
134                 NativeCrypto.ENGINE_free(ctx);
135             }
136         } finally {
137             super.finalize();
138         }
139     }
140 
141     @Override
equals(Object o)142     public boolean equals(Object o) {
143         if (o == this) {
144             return true;
145         }
146 
147         if (!(o instanceof OpenSSLEngine)) {
148             return false;
149         }
150 
151         OpenSSLEngine other = (OpenSSLEngine) o;
152 
153         if (other.getEngineContext() == ctx) {
154             return true;
155         }
156 
157         final String id = NativeCrypto.ENGINE_get_id(ctx);
158         if (id == null) {
159             return false;
160         }
161 
162         return id.equals(NativeCrypto.ENGINE_get_id(other.getEngineContext()));
163     }
164 
165     @Override
hashCode()166     public int hashCode() {
167       return (int) ctx;
168     }
169 }
170