jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/RSACipher.java
changeset 25859 3317bb8137f4
parent 23733 b9b80421cfa7
child 28091 cbd670e95e2f
--- /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;
+
+}