1 /* 2 * Copyright (c) 2014, 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.util.Arrays; 29 import java.util.WeakHashMap; 30 import java.util.Collections; 31 import java.util.Map; 32 33 import java.security.AlgorithmParameters; 34 import java.security.InvalidAlgorithmParameterException; 35 import java.security.InvalidKeyException; 36 import java.security.Key; 37 import java.security.PublicKey; 38 import java.security.PrivateKey; 39 import java.security.spec.RSAPrivateCrtKeySpec; 40 import java.security.spec.RSAPublicKeySpec; 41 import java.security.interfaces.RSAKey; 42 import java.security.interfaces.RSAPrivateCrtKey; 43 import java.security.interfaces.RSAPublicKey; 44 45 import java.security.KeyFactory; 46 import java.security.NoSuchAlgorithmException; 47 import java.security.SecureRandom; 48 49 import java.security.spec.AlgorithmParameterSpec; 50 import java.security.spec.InvalidParameterSpecException; 51 import java.security.spec.InvalidKeySpecException; 52 53 import javax.crypto.BadPaddingException; 54 import javax.crypto.Cipher; 55 import javax.crypto.CipherSpi; 56 import javax.crypto.SecretKey; 57 import javax.crypto.IllegalBlockSizeException; 58 import javax.crypto.NoSuchPaddingException; 59 import javax.crypto.ShortBufferException; 60 61 import javax.crypto.spec.SecretKeySpec; 62 63 import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; 64 import sun.security.util.KeyUtil; 65 66 /** 67 * Asymmetric Cipher wrapper class utilizing ucrypto APIs. This class 68 * currently supports 69 * - RSA/ECB/NOPADDING 70 * - RSA/ECB/PKCS1PADDING 71 * 72 * @since 1.9 73 */ 74 public class NativeRSACipher extends CipherSpi { 75 // fields set in constructor 76 private final UcryptoMech mech; 77 private final int padLen; 78 private final NativeRSAKeyFactory keyFactory; 79 private AlgorithmParameterSpec spec; 80 private SecureRandom random; 81 82 // Keep a cache of RSA keys and their RSA NativeKey for reuse. 83 // When the RSA key is gc'ed, we let NativeKey phatom references cleanup 84 // the native allocation 85 private static final Map<Key, NativeKey> keyList = 86 Collections.synchronizedMap(new WeakHashMap<Key, NativeKey>()); 87 88 // 89 // fields (re)set in every init() 90 // 91 private NativeKey key = null; 92 private int outputSize = 0; // e.g. modulus size in bytes 93 private boolean encrypt = true; 94 private byte[] buffer; 95 private int bufOfs = 0; 96 97 // public implementation classes 98 public static final class NoPadding extends NativeRSACipher { 99 public NoPadding() throws NoSuchAlgorithmException { 100 super(UcryptoMech.CRYPTO_RSA_X_509, 0); 101 } 102 } 103 104 public static final class PKCS1Padding extends NativeRSACipher { 105 public PKCS1Padding() throws NoSuchAlgorithmException { 106 super(UcryptoMech.CRYPTO_RSA_PKCS, 11); 107 } 108 } 109 110 NativeRSACipher(UcryptoMech mech, int padLen) 111 throws NoSuchAlgorithmException { 112 this.mech = mech; 113 this.padLen = padLen; 114 this.keyFactory = new NativeRSAKeyFactory(); 115 } 116 117 @Override 118 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 119 // Disallow change of mode for now since currently it's explicitly 120 // defined in transformation strings 121 throw new NoSuchAlgorithmException("Unsupported mode " + mode); 122 } 123 124 // see JCE spec 125 @Override 126 protected void engineSetPadding(String padding) 127 throws NoSuchPaddingException { 128 // Disallow change of padding for now since currently it's explicitly 129 // defined in transformation strings 130 throw new NoSuchPaddingException("Unsupported padding " + padding); 131 } 132 133 // see JCE spec 134 @Override 135 protected int engineGetBlockSize() { 136 return 0; 137 } 138 139 // see JCE spec 140 @Override 141 protected synchronized int engineGetOutputSize(int inputLen) { 142 return outputSize; 143 } 144 145 // see JCE spec 146 @Override 147 protected byte[] engineGetIV() { 148 return null; 149 } 150 151 // see JCE spec 152 @Override 153 protected AlgorithmParameters engineGetParameters() { 154 return null; 155 } 156 157 @Override 158 protected int engineGetKeySize(Key key) throws InvalidKeyException { 159 if (!(key instanceof RSAKey)) { 160 throw new InvalidKeyException("RSAKey required"); 161 } 162 int n = ((RSAKey)key).getModulus().bitLength(); 163 // strip off the leading extra 0x00 byte prefix 164 int realByteSize = (n + 7) >> 3; 165 return realByteSize * 8; 166 } 167 168 // see JCE spec 169 @Override 170 protected synchronized void engineInit(int opmode, Key key, SecureRandom random) 171 throws InvalidKeyException { 172 try { 173 engineInit(opmode, key, (AlgorithmParameterSpec)null, random); 174 } catch (InvalidAlgorithmParameterException e) { 175 throw new InvalidKeyException("init() failed", e); 176 } 177 } 178 179 // see JCE spec 180 @Override 181 @SuppressWarnings("deprecation") 182 protected synchronized void engineInit(int opmode, Key newKey, 183 AlgorithmParameterSpec params, SecureRandom random) 184 throws InvalidKeyException, InvalidAlgorithmParameterException { 185 if (newKey == null) { 186 throw new InvalidKeyException("Key cannot be null"); 187 } 188 if (opmode != Cipher.ENCRYPT_MODE && 189 opmode != Cipher.DECRYPT_MODE && 190 opmode != Cipher.WRAP_MODE && 191 opmode != Cipher.UNWRAP_MODE) { 192 throw new InvalidAlgorithmParameterException 193 ("Unsupported mode: " + opmode); 194 } 195 if (params != null) { 196 if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { 197 throw new InvalidAlgorithmParameterException( 198 "No Parameters can be specified"); 199 } 200 spec = params; 201 this.random = random; // for TLS RSA premaster secret 202 } 203 boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); 204 205 // Make sure the proper opmode uses the proper key 206 if (doEncrypt && (!(newKey instanceof RSAPublicKey))) { 207 throw new InvalidKeyException("RSAPublicKey required for encryption"); 208 } else if (!doEncrypt && (!(newKey instanceof RSAPrivateCrtKey))) { 209 throw new InvalidKeyException("RSAPrivateCrtKey required for decryption"); 210 } 211 212 NativeKey nativeKey = null; 213 // Check keyList cache for a nativeKey 214 nativeKey = keyList.get(newKey); 215 if (nativeKey == null) { 216 // With no existing nativeKey for this newKey, create one 217 if (doEncrypt) { 218 RSAPublicKey publicKey = (RSAPublicKey) newKey; 219 try { 220 nativeKey = (NativeKey) keyFactory.engineGeneratePublic 221 (new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getPublicExponent())); 222 } catch (InvalidKeySpecException ikse) { 223 throw new InvalidKeyException(ikse); 224 } 225 } else { 226 RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) newKey; 227 try { 228 nativeKey = (NativeKey) keyFactory.engineGeneratePrivate 229 (new RSAPrivateCrtKeySpec(privateKey.getModulus(), 230 privateKey.getPublicExponent(), 231 privateKey.getPrivateExponent(), 232 privateKey.getPrimeP(), 233 privateKey.getPrimeQ(), 234 privateKey.getPrimeExponentP(), 235 privateKey.getPrimeExponentQ(), 236 privateKey.getCrtCoefficient())); 237 } catch (InvalidKeySpecException ikse) { 238 throw new InvalidKeyException(ikse); 239 } 240 } 241 242 // Add nativeKey to keyList cache and associate it with newKey 243 keyList.put(newKey, nativeKey); 244 } 245 246 init(doEncrypt, nativeKey); 247 } 248 249 // see JCE spec 250 @Override 251 protected synchronized void engineInit(int opmode, Key key, AlgorithmParameters params, 252 SecureRandom random) 253 throws InvalidKeyException, InvalidAlgorithmParameterException { 254 if (params != null) { 255 throw new InvalidAlgorithmParameterException("No Parameters can be specified"); 256 } 257 engineInit(opmode, key, (AlgorithmParameterSpec) null, random); 258 } 259 260 // see JCE spec 261 @Override 262 protected synchronized byte[] engineUpdate(byte[] in, int inOfs, int inLen) { 263 if (inLen > 0) { 264 update(in, inOfs, inLen); 265 } 266 return null; 267 } 268 269 // see JCE spec 270 @Override 271 protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, 272 int outOfs) throws ShortBufferException { 273 if (out.length - outOfs < outputSize) { 274 throw new ShortBufferException("Output buffer too small"); 275 } 276 if (inLen > 0) { 277 update(in, inOfs, inLen); 278 } 279 return 0; 280 } 281 282 // see JCE spec 283 @Override 284 protected synchronized byte[] engineDoFinal(byte[] in, int inOfs, int inLen) 285 throws IllegalBlockSizeException, BadPaddingException { 286 byte[] out = new byte[outputSize]; 287 try { 288 // delegate to the other engineDoFinal(...) method 289 int actualLen = engineDoFinal(in, inOfs, inLen, out, 0); 290 if (actualLen != outputSize) { 291 return Arrays.copyOf(out, actualLen); 292 } else { 293 return out; 294 } 295 } catch (ShortBufferException e) { 296 throw new UcryptoException("Internal Error", e); 297 } 298 } 299 300 // see JCE spec 301 @Override 302 protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, 303 int outOfs) 304 throws ShortBufferException, IllegalBlockSizeException, 305 BadPaddingException { 306 if (inLen != 0) { 307 update(in, inOfs, inLen); 308 } 309 return doFinal(out, outOfs, out.length - outOfs); 310 } 311 312 313 // see JCE spec 314 @Override 315 protected synchronized byte[] engineWrap(Key key) throws IllegalBlockSizeException, 316 InvalidKeyException { 317 try { 318 byte[] encodedKey = key.getEncoded(); 319 if ((encodedKey == null) || (encodedKey.length == 0)) { 320 throw new InvalidKeyException("Cannot get an encoding of " + 321 "the key to be wrapped"); 322 } 323 if (encodedKey.length > buffer.length) { 324 throw new InvalidKeyException("Key is too long for wrapping"); 325 } 326 return engineDoFinal(encodedKey, 0, encodedKey.length); 327 } catch (BadPaddingException e) { 328 // Should never happen for key wrapping 329 throw new UcryptoException("Internal Error", e); 330 } 331 } 332 333 // see JCE spec 334 @Override 335 @SuppressWarnings("deprecation") 336 protected synchronized Key engineUnwrap(byte[] wrappedKey, 337 String wrappedKeyAlgorithm, int wrappedKeyType) 338 throws InvalidKeyException, NoSuchAlgorithmException { 339 340 if (wrappedKey.length > buffer.length) { 341 throw new InvalidKeyException("Key is too long for unwrapping"); 342 } 343 344 boolean isTlsRsaPremasterSecret = 345 wrappedKeyAlgorithm.equals("TlsRsaPremasterSecret"); 346 Exception failover = null; 347 348 byte[] encodedKey = null; 349 try { 350 encodedKey = engineDoFinal(wrappedKey, 0, wrappedKey.length); 351 } catch (BadPaddingException bpe) { 352 if (isTlsRsaPremasterSecret) { 353 failover = bpe; 354 } else { 355 throw new InvalidKeyException("Unwrapping failed", bpe); 356 } 357 } catch (Exception e) { 358 throw new InvalidKeyException("Unwrapping failed", e); 359 } 360 361 if (isTlsRsaPremasterSecret) { 362 if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { 363 throw new IllegalStateException( 364 "No TlsRsaPremasterSecretParameterSpec specified"); 365 } 366 367 // polish the TLS premaster secret 368 encodedKey = KeyUtil.checkTlsPreMasterSecretKey( 369 ((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(), 370 ((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(), 371 random, encodedKey, (failover != null)); 372 } 373 374 return NativeCipher.constructKey(wrappedKeyType, 375 encodedKey, wrappedKeyAlgorithm); 376 } 377 378 /** 379 * calls ucrypto_encrypt(...) or ucrypto_decrypt(...) 380 * @returns the length of output or an negative error status code 381 */ 382 private native static int nativeAtomic(int mech, boolean encrypt, 383 long keyValue, int keyLength, 384 byte[] in, int inLen, 385 byte[] out, int ouOfs, int outLen); 386 387 // do actual initialization 388 private void init(boolean encrypt, NativeKey key) { 389 this.encrypt = encrypt; 390 this.key = key; 391 try { 392 this.outputSize = engineGetKeySize(key)/8; 393 } catch (InvalidKeyException ike) { 394 throw new UcryptoException("Internal Error", ike); 395 } 396 this.buffer = new byte[outputSize]; 397 this.bufOfs = 0; 398 } 399 400 // store the specified input into the internal buffer 401 private void update(byte[] in, int inOfs, int inLen) { 402 if ((inLen <= 0) || (in == null)) { 403 return; 404 } 405 // buffer bytes internally until doFinal is called 406 if ((bufOfs + inLen + (encrypt? padLen:0)) > buffer.length) { 407 // lead to IllegalBlockSizeException when doFinal() is called 408 bufOfs = buffer.length + 1; 409 return; 410 } 411 System.arraycopy(in, inOfs, buffer, bufOfs, inLen); 412 bufOfs += inLen; 413 } 414 415 // return the actual non-negative output length 416 private int doFinal(byte[] out, int outOfs, int outLen) 417 throws ShortBufferException, IllegalBlockSizeException, 418 BadPaddingException { 419 if (bufOfs > buffer.length) { 420 throw new IllegalBlockSizeException( 421 "Data must not be longer than " + 422 (buffer.length - (encrypt ? padLen : 0)) + " bytes"); 423 } 424 if (outLen < outputSize) { 425 throw new ShortBufferException(); 426 } 427 try { 428 long keyValue = key.value(); 429 int k = nativeAtomic(mech.value(), encrypt, keyValue, 430 key.length(), buffer, bufOfs, 431 out, outOfs, outLen); 432 if (k < 0) { 433 if ( k == -16 || k == -64) { 434 // -16: CRYPTO_ENCRYPTED_DATA_INVALID 435 // -64: CKR_ENCRYPTED_DATA_INVALID, see bug 17459266 436 UcryptoException ue = new UcryptoException(16); 437 BadPaddingException bpe = 438 new BadPaddingException("Invalid encryption data"); 439 bpe.initCause(ue); 440 throw bpe; 441 } 442 throw new UcryptoException(-k); 443 } 444 445 return k; 446 } finally { 447 bufOfs = 0; 448 } 449 } 450 }