--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/RSACipher.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.mscapi;
+
+import java.math.BigInteger;
+import java.security.*;
+import java.security.Key;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+import sun.security.rsa.RSAKeyFactory;
+import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
+import sun.security.util.KeyUtil;
+
+/**
+ * RSA cipher implementation using the Microsoft Crypto API.
+ * Supports RSA en/decryption and signing/verifying using PKCS#1 v1.5 padding.
+ *
+ * Objects should be instantiated by calling Cipher.getInstance() using the
+ * following algorithm name:
+ *
+ * . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 padding. The mode (blocktype)
+ * is selected based on the en/decryption mode and public/private key used.
+ *
+ * We only do one RSA operation per doFinal() call. If the application passes
+ * more data via calls to update() or doFinal(), we throw an
+ * IllegalBlockSizeException when doFinal() is called (see JCE API spec).
+ * Bulk encryption using RSA does not make sense and is not standardized.
+ *
+ * Note: RSA keys should be at least 512 bits long
+ *
+ * @since 1.6
+ * @author Andreas Sterbenz
+ * @author Vincent Ryan
+ */
+public final class RSACipher extends CipherSpi {
+
+ // constant for an empty byte array
+ private final static byte[] B0 = new byte[0];
+
+ // mode constant for public key encryption
+ private final static int MODE_ENCRYPT = 1;
+ // mode constant for private key decryption
+ private final static int MODE_DECRYPT = 2;
+ // mode constant for private key encryption (signing)
+ private final static int MODE_SIGN = 3;
+ // mode constant for public key decryption (verifying)
+ private final static int MODE_VERIFY = 4;
+
+ // constant for PKCS#1 v1.5 RSA
+ private final static String PAD_PKCS1 = "PKCS1Padding";
+ private final static int PAD_PKCS1_LENGTH = 11;
+
+ // current mode, one of MODE_* above. Set when init() is called
+ private int mode;
+
+ // active padding type, one of PAD_* above. Set by setPadding()
+ private String paddingType;
+ private int paddingLength = 0;
+
+ // buffer for the data
+ private byte[] buffer;
+ // offset into the buffer (number of bytes buffered)
+ private int bufOfs;
+
+ // size of the output (the length of the key).
+ private int outputSize;
+
+ // the public key, if we were initialized using a public key
+ private sun.security.mscapi.Key publicKey;
+
+ // the private key, if we were initialized using a private key
+ private sun.security.mscapi.Key privateKey;
+
+ // cipher parameter for TLS RSA premaster secret
+ private AlgorithmParameterSpec spec = null;
+
+ // the source of randomness
+ private SecureRandom random;
+
+ public RSACipher() {
+ paddingType = PAD_PKCS1;
+ }
+
+ // modes do not make sense for RSA, but allow ECB
+ // see JCE spec
+ protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+ if (mode.equalsIgnoreCase("ECB") == false) {
+ throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+ }
+ }
+
+ // set the padding type
+ // see JCE spec
+ protected void engineSetPadding(String paddingName)
+ throws NoSuchPaddingException {
+ if (paddingName.equalsIgnoreCase(PAD_PKCS1)) {
+ paddingType = PAD_PKCS1;
+ } else {
+ throw new NoSuchPaddingException
+ ("Padding " + paddingName + " not supported");
+ }
+ }
+
+ // return 0 as block size, we are not a block cipher
+ // see JCE spec
+ protected int engineGetBlockSize() {
+ return 0;
+ }
+
+ // return the output size
+ // see JCE spec
+ protected int engineGetOutputSize(int inputLen) {
+ return outputSize;
+ }
+
+ // no iv, return null
+ // see JCE spec
+ protected byte[] engineGetIV() {
+ return null;
+ }
+
+ // no parameters, return null
+ // see JCE spec
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ init(opmode, key);
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key,
+ AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
+ throw new InvalidAlgorithmParameterException(
+ "Parameters not supported");
+ }
+ spec = params;
+ this.random = random; // for TLS RSA premaster secret
+ }
+ init(opmode, key);
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key,
+ AlgorithmParameters params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException
+ ("Parameters not supported");
+ }
+ init(opmode, key);
+ }
+
+ // initialize this cipher
+ private void init(int opmode, Key key) throws InvalidKeyException {
+
+ boolean encrypt;
+
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ case Cipher.WRAP_MODE:
+ paddingLength = PAD_PKCS1_LENGTH;
+ encrypt = true;
+ break;
+ case Cipher.DECRYPT_MODE:
+ case Cipher.UNWRAP_MODE:
+ paddingLength = 0; // reset
+ encrypt = false;
+ break;
+ default:
+ throw new InvalidKeyException("Unknown mode: " + opmode);
+ }
+
+ if (!(key instanceof sun.security.mscapi.Key)) {
+ if (key instanceof java.security.interfaces.RSAPublicKey) {
+ java.security.interfaces.RSAPublicKey rsaKey =
+ (java.security.interfaces.RSAPublicKey) key;
+
+ // Convert key to MSCAPI format
+
+ BigInteger modulus = rsaKey.getModulus();
+ BigInteger exponent = rsaKey.getPublicExponent();
+
+ // Check against the local and global values to make sure
+ // the sizes are ok. Round up to the nearest byte.
+ RSAKeyFactory.checkKeyLengths(((modulus.bitLength() + 7) & ~7),
+ exponent, -1, RSAKeyPairGenerator.KEY_SIZE_MAX);
+
+ byte[] modulusBytes = modulus.toByteArray();
+ byte[] exponentBytes = exponent.toByteArray();
+
+ // Adjust key length due to sign bit
+ int keyBitLength = (modulusBytes[0] == 0)
+ ? (modulusBytes.length - 1) * 8
+ : modulusBytes.length * 8;
+
+ byte[] keyBlob = RSASignature.generatePublicKeyBlob(
+ keyBitLength, modulusBytes, exponentBytes);
+
+ try {
+ key = RSASignature.importPublicKey(keyBlob, keyBitLength);
+
+ } catch (KeyStoreException e) {
+ throw new InvalidKeyException(e);
+ }
+
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key);
+ }
+ }
+
+ if (key instanceof PublicKey) {
+ mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
+ publicKey = (sun.security.mscapi.Key)key;
+ privateKey = null;
+ outputSize = publicKey.length() / 8;
+ } else if (key instanceof PrivateKey) {
+ mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
+ privateKey = (sun.security.mscapi.Key)key;
+ publicKey = null;
+ outputSize = privateKey.length() / 8;
+ } else {
+ throw new InvalidKeyException("Unknown key type: " + key);
+ }
+
+ bufOfs = 0;
+ buffer = new byte[outputSize];
+ }
+
+ // internal update method
+ private void update(byte[] in, int inOfs, int inLen) {
+ if ((inLen == 0) || (in == null)) {
+ return;
+ }
+ if (bufOfs + inLen > (buffer.length - paddingLength)) {
+ bufOfs = buffer.length + 1;
+ return;
+ }
+ System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
+ bufOfs += inLen;
+ }
+
+ // internal doFinal() method. Here we perform the actual RSA operation
+ private byte[] doFinal() throws BadPaddingException,
+ IllegalBlockSizeException {
+ if (bufOfs > buffer.length) {
+ throw new IllegalBlockSizeException("Data must not be longer "
+ + "than " + (buffer.length - paddingLength) + " bytes");
+ }
+
+ try {
+ byte[] data = buffer;
+ switch (mode) {
+ case MODE_SIGN:
+ return encryptDecrypt(data, bufOfs,
+ privateKey.getHCryptKey(), true);
+
+ case MODE_VERIFY:
+ return encryptDecrypt(data, bufOfs,
+ publicKey.getHCryptKey(), false);
+
+ case MODE_ENCRYPT:
+ return encryptDecrypt(data, bufOfs,
+ publicKey.getHCryptKey(), true);
+
+ case MODE_DECRYPT:
+ return encryptDecrypt(data, bufOfs,
+ privateKey.getHCryptKey(), false);
+
+ default:
+ throw new AssertionError("Internal error");
+ }
+
+ } catch (KeyException e) {
+ throw new ProviderException(e);
+
+ } finally {
+ bufOfs = 0;
+ }
+ }
+
+ // see JCE spec
+ protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
+ update(in, inOfs, inLen);
+ return B0;
+ }
+
+ // see JCE spec
+ protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
+ int outOfs) {
+ update(in, inOfs, inLen);
+ return 0;
+ }
+
+ // see JCE spec
+ protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
+ throws BadPaddingException, IllegalBlockSizeException {
+ update(in, inOfs, inLen);
+ return doFinal();
+ }
+
+ // see JCE spec
+ protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
+ int outOfs) throws ShortBufferException, BadPaddingException,
+ IllegalBlockSizeException {
+ if (outputSize > out.length - outOfs) {
+ throw new ShortBufferException
+ ("Need " + outputSize + " bytes for output");
+ }
+ update(in, inOfs, inLen);
+ byte[] result = doFinal();
+ int n = result.length;
+ System.arraycopy(result, 0, out, outOfs, n);
+ return n;
+ }
+
+ // see JCE spec
+ protected byte[] engineWrap(Key key) throws InvalidKeyException,
+ IllegalBlockSizeException {
+ byte[] encoded = key.getEncoded(); // TODO - unextractable key
+ if ((encoded == null) || (encoded.length == 0)) {
+ throw new InvalidKeyException("Could not obtain encoded key");
+ }
+ if (encoded.length > buffer.length) {
+ throw new InvalidKeyException("Key is too long for wrapping");
+ }
+ update(encoded, 0, encoded.length);
+ try {
+ return doFinal();
+ } catch (BadPaddingException e) {
+ // should not occur
+ throw new InvalidKeyException("Wrapping failed", e);
+ }
+ }
+
+ // see JCE spec
+ protected java.security.Key engineUnwrap(byte[] wrappedKey,
+ String algorithm,
+ int type) throws InvalidKeyException, NoSuchAlgorithmException {
+
+ if (wrappedKey.length > buffer.length) {
+ throw new InvalidKeyException("Key is too long for unwrapping");
+ }
+
+ boolean isTlsRsaPremasterSecret =
+ algorithm.equals("TlsRsaPremasterSecret");
+ Exception failover = null;
+ byte[] encoded = null;
+
+ update(wrappedKey, 0, wrappedKey.length);
+ try {
+ encoded = doFinal();
+ } catch (BadPaddingException e) {
+ if (isTlsRsaPremasterSecret) {
+ failover = e;
+ } else {
+ throw new InvalidKeyException("Unwrapping failed", e);
+ }
+ } catch (IllegalBlockSizeException e) {
+ // should not occur, handled with length check above
+ throw new InvalidKeyException("Unwrapping failed", e);
+ }
+
+ if (isTlsRsaPremasterSecret) {
+ if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
+ throw new IllegalStateException(
+ "No TlsRsaPremasterSecretParameterSpec specified");
+ }
+
+ // polish the TLS premaster secret
+ encoded = KeyUtil.checkTlsPreMasterSecretKey(
+ ((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(),
+ ((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(),
+ random, encoded, (failover != null));
+ }
+
+ return constructKey(encoded, algorithm, type);
+ }
+
+ // see JCE spec
+ protected int engineGetKeySize(Key key) throws InvalidKeyException {
+
+ if (key instanceof sun.security.mscapi.Key) {
+ return ((sun.security.mscapi.Key) key).length();
+
+ } else if (key instanceof RSAKey) {
+ return ((RSAKey) key).getModulus().bitLength();
+
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key);
+ }
+ }
+
+ // Construct an X.509 encoded public key.
+ private static PublicKey constructPublicKey(byte[] encodedKey,
+ String encodedKeyAlgorithm)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm);
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
+
+ return keyFactory.generatePublic(keySpec);
+
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new NoSuchAlgorithmException("No installed provider " +
+ "supports the " + encodedKeyAlgorithm + " algorithm", nsae);
+
+ } catch (InvalidKeySpecException ike) {
+ throw new InvalidKeyException("Cannot construct public key", ike);
+ }
+ }
+
+ // Construct a PKCS #8 encoded private key.
+ private static PrivateKey constructPrivateKey(byte[] encodedKey,
+ String encodedKeyAlgorithm)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm);
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
+
+ return keyFactory.generatePrivate(keySpec);
+
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new NoSuchAlgorithmException("No installed provider " +
+ "supports the " + encodedKeyAlgorithm + " algorithm", nsae);
+
+ } catch (InvalidKeySpecException ike) {
+ throw new InvalidKeyException("Cannot construct private key", ike);
+ }
+ }
+
+ // Construct an encoded secret key.
+ private static SecretKey constructSecretKey(byte[] encodedKey,
+ String encodedKeyAlgorithm) {
+
+ return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
+ }
+
+ private static Key constructKey(byte[] encodedKey,
+ String encodedKeyAlgorithm,
+ int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
+
+ switch (keyType) {
+ case Cipher.PUBLIC_KEY:
+ return constructPublicKey(encodedKey, encodedKeyAlgorithm);
+ case Cipher.PRIVATE_KEY:
+ return constructPrivateKey(encodedKey, encodedKeyAlgorithm);
+ case Cipher.SECRET_KEY:
+ return constructSecretKey(encodedKey, encodedKeyAlgorithm);
+ default:
+ throw new InvalidKeyException("Unknown key type " + keyType);
+ }
+ }
+
+ /*
+ * Encrypt/decrypt a data buffer using Microsoft Crypto API with HCRYPTKEY.
+ * It expects and returns ciphertext data in big-endian form.
+ */
+ private native static byte[] encryptDecrypt(byte[] data, int dataSize,
+ long hCryptKey, boolean doEncrypt) throws KeyException;
+
+}