NativeRSACipher.java revision 10854:558e97e47abe
1251607Sdim/* 2251607Sdim * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3251607Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4251607Sdim * 5251607Sdim * This code is free software; you can redistribute it and/or modify it 6251607Sdim * under the terms of the GNU General Public License version 2 only, as 7251607Sdim * published by the Free Software Foundation. Oracle designates this 8251607Sdim * particular file as subject to the "Classpath" exception as provided 9251607Sdim * by Oracle in the LICENSE file that accompanied this code. 10251607Sdim * 11251607Sdim * This code is distributed in the hope that it will be useful, but WITHOUT 12251607Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13251607Sdim * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14251607Sdim * version 2 for more details (a copy is included in the LICENSE file that 15251607Sdim * accompanied this code). 16251607Sdim * 17251607Sdim * You should have received a copy of the GNU General Public License version 18251607Sdim * 2 along with this work; if not, write to the Free Software Foundation, 19251607Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20251607Sdim * 21251607Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22251607Sdim * or visit www.oracle.com if you need additional information or have any 23251607Sdim * questions. 24251607Sdim */ 25251607Sdim 26251607Sdimpackage com.oracle.security.ucrypto; 27251607Sdim 28251607Sdimimport java.util.Arrays; 29251607Sdimimport java.util.WeakHashMap; 30251607Sdimimport java.util.Collections; 31251607Sdimimport java.util.Map; 32251607Sdim 33251607Sdimimport java.security.AlgorithmParameters; 34251607Sdimimport java.security.InvalidAlgorithmParameterException; 35251607Sdimimport java.security.InvalidKeyException; 36251607Sdimimport java.security.Key; 37251607Sdimimport java.security.PublicKey; 38251607Sdimimport java.security.PrivateKey; 39251607Sdimimport java.security.spec.RSAPrivateCrtKeySpec; 40251607Sdimimport java.security.spec.RSAPublicKeySpec; 41251607Sdimimport java.security.interfaces.RSAKey; 42251607Sdimimport java.security.interfaces.RSAPrivateCrtKey; 43251607Sdimimport java.security.interfaces.RSAPublicKey; 44251607Sdim 45251607Sdimimport java.security.KeyFactory; 46251607Sdimimport java.security.NoSuchAlgorithmException; 47import java.security.SecureRandom; 48 49import java.security.spec.AlgorithmParameterSpec; 50import java.security.spec.InvalidParameterSpecException; 51import java.security.spec.InvalidKeySpecException; 52 53import javax.crypto.BadPaddingException; 54import javax.crypto.Cipher; 55import javax.crypto.CipherSpi; 56import javax.crypto.SecretKey; 57import javax.crypto.IllegalBlockSizeException; 58import javax.crypto.NoSuchPaddingException; 59import javax.crypto.ShortBufferException; 60 61import javax.crypto.spec.SecretKeySpec; 62 63import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; 64import 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 */ 74public 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 protected synchronized void engineInit(int opmode, Key newKey, 182 AlgorithmParameterSpec params, SecureRandom random) 183 throws InvalidKeyException, InvalidAlgorithmParameterException { 184 if (newKey == null) { 185 throw new InvalidKeyException("Key cannot be null"); 186 } 187 if (opmode != Cipher.ENCRYPT_MODE && 188 opmode != Cipher.DECRYPT_MODE && 189 opmode != Cipher.WRAP_MODE && 190 opmode != Cipher.UNWRAP_MODE) { 191 throw new InvalidAlgorithmParameterException 192 ("Unsupported mode: " + opmode); 193 } 194 if (params != null) { 195 if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { 196 throw new InvalidAlgorithmParameterException( 197 "No Parameters can be specified"); 198 } 199 spec = params; 200 this.random = random; // for TLS RSA premaster secret 201 } 202 boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); 203 204 // Make sure the proper opmode uses the proper key 205 if (doEncrypt && (!(newKey instanceof RSAPublicKey))) { 206 throw new InvalidKeyException("RSAPublicKey required for encryption"); 207 } else if (!doEncrypt && (!(newKey instanceof RSAPrivateCrtKey))) { 208 throw new InvalidKeyException("RSAPrivateCrtKey required for decryption"); 209 } 210 211 NativeKey nativeKey = null; 212 // Check keyList cache for a nativeKey 213 nativeKey = keyList.get(newKey); 214 if (nativeKey == null) { 215 // With no existing nativeKey for this newKey, create one 216 if (doEncrypt) { 217 RSAPublicKey publicKey = (RSAPublicKey) newKey; 218 try { 219 nativeKey = (NativeKey) keyFactory.engineGeneratePublic 220 (new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getPublicExponent())); 221 } catch (InvalidKeySpecException ikse) { 222 throw new InvalidKeyException(ikse); 223 } 224 } else { 225 RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) newKey; 226 try { 227 nativeKey = (NativeKey) keyFactory.engineGeneratePrivate 228 (new RSAPrivateCrtKeySpec(privateKey.getModulus(), 229 privateKey.getPublicExponent(), 230 privateKey.getPrivateExponent(), 231 privateKey.getPrimeP(), 232 privateKey.getPrimeQ(), 233 privateKey.getPrimeExponentP(), 234 privateKey.getPrimeExponentQ(), 235 privateKey.getCrtCoefficient())); 236 } catch (InvalidKeySpecException ikse) { 237 throw new InvalidKeyException(ikse); 238 } 239 } 240 241 // Add nativeKey to keyList cache and associate it with newKey 242 keyList.put(newKey, nativeKey); 243 } 244 245 init(doEncrypt, nativeKey); 246 } 247 248 // see JCE spec 249 @Override 250 protected synchronized void engineInit(int opmode, Key key, AlgorithmParameters params, 251 SecureRandom random) 252 throws InvalidKeyException, InvalidAlgorithmParameterException { 253 if (params != null) { 254 throw new InvalidAlgorithmParameterException("No Parameters can be specified"); 255 } 256 engineInit(opmode, key, (AlgorithmParameterSpec) null, random); 257 } 258 259 // see JCE spec 260 @Override 261 protected synchronized byte[] engineUpdate(byte[] in, int inOfs, int inLen) { 262 if (inLen > 0) { 263 update(in, inOfs, inLen); 264 } 265 return null; 266 } 267 268 // see JCE spec 269 @Override 270 protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, 271 int outOfs) throws ShortBufferException { 272 if (out.length - outOfs < outputSize) { 273 throw new ShortBufferException("Output buffer too small"); 274 } 275 if (inLen > 0) { 276 update(in, inOfs, inLen); 277 } 278 return 0; 279 } 280 281 // see JCE spec 282 @Override 283 protected synchronized byte[] engineDoFinal(byte[] in, int inOfs, int inLen) 284 throws IllegalBlockSizeException, BadPaddingException { 285 byte[] out = new byte[outputSize]; 286 try { 287 // delegate to the other engineDoFinal(...) method 288 int actualLen = engineDoFinal(in, inOfs, inLen, out, 0); 289 if (actualLen != outputSize) { 290 return Arrays.copyOf(out, actualLen); 291 } else { 292 return out; 293 } 294 } catch (ShortBufferException e) { 295 throw new UcryptoException("Internal Error", e); 296 } 297 } 298 299 // see JCE spec 300 @Override 301 protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, 302 int outOfs) 303 throws ShortBufferException, IllegalBlockSizeException, 304 BadPaddingException { 305 if (inLen != 0) { 306 update(in, inOfs, inLen); 307 } 308 return doFinal(out, outOfs, out.length - outOfs); 309 } 310 311 312 // see JCE spec 313 @Override 314 protected synchronized byte[] engineWrap(Key key) throws IllegalBlockSizeException, 315 InvalidKeyException { 316 try { 317 byte[] encodedKey = key.getEncoded(); 318 if ((encodedKey == null) || (encodedKey.length == 0)) { 319 throw new InvalidKeyException("Cannot get an encoding of " + 320 "the key to be wrapped"); 321 } 322 if (encodedKey.length > buffer.length) { 323 throw new InvalidKeyException("Key is too long for wrapping"); 324 } 325 return engineDoFinal(encodedKey, 0, encodedKey.length); 326 } catch (BadPaddingException e) { 327 // Should never happen for key wrapping 328 throw new UcryptoException("Internal Error", e); 329 } 330 } 331 332 // see JCE spec 333 @Override 334 protected synchronized Key engineUnwrap(byte[] wrappedKey, 335 String wrappedKeyAlgorithm, int wrappedKeyType) 336 throws InvalidKeyException, NoSuchAlgorithmException { 337 338 if (wrappedKey.length > buffer.length) { 339 throw new InvalidKeyException("Key is too long for unwrapping"); 340 } 341 342 boolean isTlsRsaPremasterSecret = 343 wrappedKeyAlgorithm.equals("TlsRsaPremasterSecret"); 344 Exception failover = null; 345 346 byte[] encodedKey = null; 347 try { 348 encodedKey = engineDoFinal(wrappedKey, 0, wrappedKey.length); 349 } catch (BadPaddingException bpe) { 350 if (isTlsRsaPremasterSecret) { 351 failover = bpe; 352 } else { 353 throw new InvalidKeyException("Unwrapping failed", bpe); 354 } 355 } catch (Exception e) { 356 throw new InvalidKeyException("Unwrapping failed", e); 357 } 358 359 if (isTlsRsaPremasterSecret) { 360 if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { 361 throw new IllegalStateException( 362 "No TlsRsaPremasterSecretParameterSpec specified"); 363 } 364 365 // polish the TLS premaster secret 366 encodedKey = KeyUtil.checkTlsPreMasterSecretKey( 367 ((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(), 368 ((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(), 369 random, encodedKey, (failover != null)); 370 } 371 372 return NativeCipher.constructKey(wrappedKeyType, 373 encodedKey, wrappedKeyAlgorithm); 374 } 375 376 /** 377 * calls ucrypto_encrypt(...) or ucrypto_decrypt(...) 378 * @returns the length of output or an negative error status code 379 */ 380 private native static int nativeAtomic(int mech, boolean encrypt, 381 long keyValue, int keyLength, 382 byte[] in, int inLen, 383 byte[] out, int ouOfs, int outLen); 384 385 // do actual initialization 386 private void init(boolean encrypt, NativeKey key) { 387 this.encrypt = encrypt; 388 this.key = key; 389 try { 390 this.outputSize = engineGetKeySize(key)/8; 391 } catch (InvalidKeyException ike) { 392 throw new UcryptoException("Internal Error", ike); 393 } 394 this.buffer = new byte[outputSize]; 395 this.bufOfs = 0; 396 } 397 398 // store the specified input into the internal buffer 399 private void update(byte[] in, int inOfs, int inLen) { 400 if ((inLen <= 0) || (in == null)) { 401 return; 402 } 403 // buffer bytes internally until doFinal is called 404 if ((bufOfs + inLen + (encrypt? padLen:0)) > buffer.length) { 405 // lead to IllegalBlockSizeException when doFinal() is called 406 bufOfs = buffer.length + 1; 407 return; 408 } 409 System.arraycopy(in, inOfs, buffer, bufOfs, inLen); 410 bufOfs += inLen; 411 } 412 413 // return the actual non-negative output length 414 private int doFinal(byte[] out, int outOfs, int outLen) 415 throws ShortBufferException, IllegalBlockSizeException, 416 BadPaddingException { 417 if (bufOfs > buffer.length) { 418 throw new IllegalBlockSizeException( 419 "Data must not be longer than " + 420 (buffer.length - (encrypt ? padLen : 0)) + " bytes"); 421 } 422 if (outLen < outputSize) { 423 throw new ShortBufferException(); 424 } 425 try { 426 long keyValue = key.value(); 427 int k = nativeAtomic(mech.value(), encrypt, keyValue, 428 key.length(), buffer, bufOfs, 429 out, outOfs, outLen); 430 if (k < 0) { 431 if ( k == -16 || k == -64) { 432 // -16: CRYPTO_ENCRYPTED_DATA_INVALID 433 // -64: CKR_ENCRYPTED_DATA_INVALID, see bug 17459266 434 UcryptoException ue = new UcryptoException(16); 435 BadPaddingException bpe = 436 new BadPaddingException("Invalid encryption data"); 437 bpe.initCause(ue); 438 throw bpe; 439 } 440 throw new UcryptoException(-k); 441 } 442 443 return k; 444 } finally { 445 bufOfs = 0; 446 } 447 } 448} 449