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 
  26 package com.oracle.security.ucrypto;
  27 
  28 import java.io.IOException;
  29 import java.io.File;
  30 import java.util.*;
  31 import java.security.*;
  32 
  33 /**
  34  * OracleUcrypto provider main class.
  35  *
  36  * @since 1.9
  37  */
  38 public 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 }