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