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