UcryptoProvider.java revision 12245:2f69eb7d4b90
1/*
2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.oracle.security.ucrypto;
27
28import java.io.IOException;
29import java.io.File;
30import java.util.*;
31import java.security.*;
32
33/**
34 * OracleUcrypto provider main class.
35 *
36 * @since 1.9
37 */
38public final class UcryptoProvider extends Provider {
39
40    private static final long serialVersionUID = 351251234302833L;
41
42    private static boolean DEBUG = false;
43    private static HashMap<String, ServiceDesc> provProp = null;
44    private static String defConfigName = "";
45
46    static {
47        try {
48            // cannot use LoadLibraryAction because that would make the native
49            // library available to the bootclassloader, but we run in the
50            // extension classloader.
51            String osname = System.getProperty("os.name");
52            if (osname.startsWith("SunOS")) {
53                provProp = AccessController.doPrivileged
54                    (new PrivilegedAction<HashMap<String, ServiceDesc>>() {
55                        public HashMap<String, ServiceDesc> run() {
56                            try {
57                                DEBUG = Boolean.parseBoolean(System.getProperty("com.oracle.security.ucrypto.debug"));
58                                String javaHome = System.getProperty("java.home");
59                                String sep = System.getProperty("file.separator");
60                                defConfigName = javaHome + sep + "conf" + sep + "security" + sep +
61                                    "ucrypto-solaris.cfg";
62                                System.loadLibrary("j2ucrypto");
63                                return new HashMap<>();
64                            } catch (Error err) {
65                                if (DEBUG) err.printStackTrace();
66                                return null;
67                            } catch (SecurityException se) {
68                                if (DEBUG) se.printStackTrace();
69                                return null;
70                            }
71                        }
72                    });
73            }
74            if (provProp != null) {
75                boolean[] result = loadLibraries();
76                if (result.length == 2) {
77                    if (result[0]) { // successfully loaded libmd
78                        provProp.put("MessageDigest.MD5",
79                            sd("MessageDigest", "MD5",
80                               "com.oracle.security.ucrypto.NativeDigest$MD5"));
81                        provProp.put("MessageDigest.SHA",
82                            sd("MessageDigest", "SHA",
83                               "com.oracle.security.ucrypto.NativeDigest$SHA1",
84                               "SHA-1", "SHA1"));
85                        provProp.put("MessageDigest.SHA-256",
86                            sd("MessageDigest", "SHA-256",
87                               "com.oracle.security.ucrypto.NativeDigest$SHA256",
88                               "2.16.840.1.101.3.4.2.1", "OID.2.16.840.1.101.3.4.2.1"));
89
90                        provProp.put("MessageDigest.SHA-384",
91                            sd("MessageDigest", "SHA-384",
92                               "com.oracle.security.ucrypto.NativeDigest$SHA384",
93                               "2.16.840.1.101.3.4.2.2", "OID.2.16.840.1.101.3.4.2.2"));
94
95                        provProp.put("MessageDigest.SHA-512",
96                            sd("MessageDigest", "SHA-512",
97                               "com.oracle.security.ucrypto.NativeDigest$SHA512",
98                               "2.16.840.1.101.3.4.2.3", "OID.2.16.840.1.101.3.4.2.3"));
99                    };
100                    if (result[1]) { // successfully loaded libsoftcrypto
101                        String supportedMechs = getMechList();
102                        debug("Prov: supported mechs = " + supportedMechs);
103                        for (UcryptoMech m : UcryptoMech.values()) {
104                            if (supportedMechs.indexOf(m.name() + ",") != -1) {
105                                ServiceDesc[] services = m.getServiceDescriptions();
106                                // skip unsupported UcryptoMech
107                                if (services == null || services.length == 0) continue;
108                                for (int p = 0; p < services.length; p++) {
109                                    ServiceDesc entry = services[p];
110                                    provProp.put(entry.getType() + "." + entry.getAlgorithm(),
111                                                 entry);
112                                }
113                            }
114                        }
115                        // NOTE: GCM support is only available since jdk 7
116                        provProp.put("AlgorithmParameters.GCM",
117                                     sd("AlgorithmParameters", "GCM", "com.oracle.security.ucrypto.GCMParameters"));
118                    }
119                } else {
120                    debug("Prov: unexpected ucrypto library loading error, got " + result.length);
121                }
122            }
123        } catch (AccessControlException ace) {
124            if (DEBUG) ace.printStackTrace();
125            // disable Ucrypto provider
126            provProp = null;
127        }
128    }
129
130    private static ServiceDesc sd(String type, String algo, String cn,
131        String... aliases) {
132        return new ServiceDesc(type, algo, cn, aliases);
133    }
134
135    private static final class ProviderService extends Provider.Service {
136        ProviderService(Provider p, ServiceDesc sd) {
137            super(p, sd.getType(), sd.getAlgorithm(), sd.getClassName(),
138                  sd.getAliases(), null);
139        }
140
141        @Override
142        public Object newInstance(Object ctrParamObj)
143            throws NoSuchAlgorithmException {
144            String type = getType();
145            if (ctrParamObj != null) {
146                throw new InvalidParameterException
147                    ("constructorParameter not used with " + type + " engines");
148            }
149            String algo = getAlgorithm();
150            try {
151                if (type.equals("Cipher")) {
152                    int keySize = -1;
153                    if (algo.charAt(3) == '_') {
154                        keySize = Integer.parseInt(algo.substring(4, 7))/8;
155                        algo = algo.substring(0, 3) + algo.substring(7);
156                    }
157                    if (algo.equals("AES/ECB/NoPadding")) {
158                        return new NativeCipher.AesEcbNoPadding(keySize);
159                    } else if (algo.equals("AES/ECB/PKCS5Padding")) {
160                        return new NativeCipherWithJavaPadding.AesEcbPKCS5();
161                    } else if (algo.equals("AES/CBC/NoPadding")) {
162                        return new NativeCipher.AesCbcNoPadding(keySize);
163                    } else if (algo.equals("AES/CBC/PKCS5Padding")) {
164                        return new NativeCipherWithJavaPadding.AesCbcPKCS5();
165                    } else if (algo.equals("AES/CTR/NoPadding")) {
166                        return new NativeCipher.AesCtrNoPadding();
167                    } else if (algo.equals("AES/GCM/NoPadding")) {
168                        return new NativeGCMCipher.AesGcmNoPadding(keySize);
169                    } else if (algo.equals("AES/CFB128/NoPadding")) {
170                        return new NativeCipher.AesCfb128NoPadding();
171                    } else if (algo.equals("AES/CFB128/PKCS5Padding")) {
172                        return new NativeCipherWithJavaPadding.AesCfb128PKCS5();
173                    } else if (algo.equals("RSA/ECB/NoPadding")) {
174                        return new NativeRSACipher.NoPadding();
175                    } else if (algo.equals("RSA/ECB/PKCS1Padding")) {
176                        return new NativeRSACipher.PKCS1Padding();
177                    }
178                } else if (type.equals("Signature")) {
179                    if (algo.equals("SHA1withRSA")) {
180                        return new NativeRSASignature.SHA1();
181                    } else if (algo.equals("SHA256withRSA")) {
182                        return new NativeRSASignature.SHA256();
183                    } else if (algo.equals("SHA384withRSA")) {
184                        return new NativeRSASignature.SHA384();
185                    } else if (algo.equals("SHA512withRSA")) {
186                        return new NativeRSASignature.SHA512();
187                    } else if (algo.equals("MD5withRSA")) {
188                        return new NativeRSASignature.MD5();
189                    }
190                } else if (type.equals("MessageDigest")) {
191                    if (algo.equals("SHA")) {
192                        return new NativeDigest.SHA1();
193                    } else if (algo.equals("SHA-256")) {
194                        return new NativeDigest.SHA256();
195                    } else if (algo.equals("SHA-384")) {
196                        return new NativeDigest.SHA384();
197                    } else if (algo.equals("SHA-512")) {
198                        return new NativeDigest.SHA512();
199                    } else if (algo.equals("MD5")) {
200                        return new NativeDigest.MD5();
201                    }
202                } else if (type.equals("AlgorithmParameters")) {
203                    if (algo.equals("GCM")) {
204                        return new GCMParameters();
205                    }
206                }
207            } catch (Exception ex) {
208                throw new NoSuchAlgorithmException("Error constructing " +
209                    type + " for " + algo + " using OracleUcrypto", ex);
210            }
211            throw new ProviderException("No impl for " + algo +
212                " " + type);
213        }
214    }
215
216    static Provider provider = null;
217    private static native boolean[] loadLibraries();
218    private static native String getMechList();
219
220    static void debug(String msg) {
221        if (DEBUG) {
222            System.out.println("UCrypto/" + msg);
223        }
224    }
225
226    public UcryptoProvider() {
227        super("OracleUcrypto", 1.9d, "Provider using Oracle Ucrypto API");
228
229        AccessController.doPrivileged(new PrivilegedAction<>() {
230            public Void run() {
231                init(defConfigName);
232                return null;
233            }
234        });
235        if (provider == null) provider = this;
236    }
237
238    private void init(final String configName) {
239        if (provProp != null) {
240            debug("Prov: configuration file " + configName);
241            Config c;
242            try {
243                c = new Config(configName);
244            } catch (Exception ex) {
245                throw new UcryptoException("Error parsing Config", ex);
246            }
247
248            String[] disabledServices = c.getDisabledServices();
249            for (String ds : disabledServices) {
250                if (provProp.remove(ds) != null) {
251                    debug("Prov: remove config-disabled service " + ds);
252                } else {
253                    debug("Prov: ignore unsupported service " + ds);
254                }
255            }
256
257            for (ServiceDesc s: provProp.values()) {
258                debug("Prov: add service for " + s);
259                putService(new ProviderService(this, s));
260            }
261        }
262    }
263
264    @Override
265    public Provider configure(String configArg) throws InvalidParameterException {
266        // default policy entry only grants read access to default config
267        if (!defConfigName.equals(configArg)) {
268            throw new InvalidParameterException("Ucrypto provider can only be " +
269                "configured with default configuration file");
270        }
271        // re-read the config
272        init(defConfigName);
273        return this;
274    }
275
276    public boolean equals(Object obj) {
277        return this == obj;
278    }
279
280    public int hashCode() {
281        return System.identityHashCode(this);
282    }
283}
284