--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.services.jce.JCECipherProvider\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.services.jce;\r
+\r
+import org.apache.derby.iapi.services.crypto.CipherFactory;\r
+import org.apache.derby.iapi.services.crypto.CipherProvider;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import java.security.Key;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.GeneralSecurityException;\r
+import java.security.NoSuchProviderException;\r
+\r
+import javax.crypto.Cipher;\r
+import javax.crypto.spec.IvParameterSpec;\r
+import javax.crypto.SecretKeyFactory;\r
+import javax.crypto.spec.SecretKeySpec;\r
+import javax.crypto.SecretKey;\r
+\r
+\r
+/**\r
+ This is a wrapper for a Cipher\r
+\r
+ @see CipherFactory\r
+ */\r
+class JCECipherProvider implements CipherProvider\r
+{\r
+ private Cipher cipher;\r
+ private int mode;\r
+ private boolean ivUsed = true;\r
+ private final IvParameterSpec ivspec;\r
+ private final int encryptionBlockSize;\r
+ private boolean sunjce; //default of bool is false\r
+\r
+ // other provider workaround, we need to re-init the cipher before every encrypt/decrypt\r
+ private SecretKey cryptixKey;\r
+\r
+ JCECipherProvider(int mode, SecretKey secretKey, byte[] iv, String algorithm, String provider)\r
+ throws StandardException\r
+ {\r
+ Throwable t;\r
+ ivspec = new IvParameterSpec(iv);\r
+ try\r
+ {\r
+\r
+\r
+ if (provider == null)\r
+ {\r
+ cipher = Cipher.getInstance(algorithm);\r
+\r
+ // see below.\r
+ if ("SunJCE".equals(cipher.getProvider().getName()))\r
+ sunjce = true;\r
+ }\r
+ else\r
+ {\r
+ /* The Sun encryption provider does not need to re-init the cipher\r
+ * after each encrypt/decrypt. This is a speed up trick.\r
+ * Other crypto providers needs this because the encrypt/decrypt\r
+ * ciphers becomes out of sync after an encrypt/decrypt operation.\r
+ */\r
+ if( provider.equals("SunJCE") )\r
+ {\r
+ sunjce = true;\r
+ }\r
+ else\r
+ {\r
+ /* The BouncyCastle encryption provider is named "BC".\r
+ * The full "BouncyCastleProvider" name used to work until\r
+ * version 103 came out. (ie. Beta3 and Beta4 works fine)\r
+ * This trick is so that Cipher.getInstance(algo, prov) will\r
+ * not throw an exception. Resolve 3765.\r
+ */\r
+ if( provider.equals( "BouncyCastleProvider" ) )\r
+ provider = "BC";\r
+ }\r
+\r
+ cipher = Cipher.getInstance(algorithm,provider);\r
+ }\r
+\r
+ // At creation time, the encryption block size is stored in order\r
+ // to do appropriate padding\r
+ encryptionBlockSize = cipher.getBlockSize();\r
+\r
+ this.mode = mode;\r
+ try {\r
+\r
+ // ECB feedback mode does not require an IV\r
+ if (mode == CipherFactory.ENCRYPT)\r
+ {\r
+ if ((algorithm.indexOf("/ECB") > -1))\r
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);\r
+ else\r
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey,ivspec);\r
+ }\r
+ else if (mode == CipherFactory.DECRYPT)\r
+ {\r
+ if ((algorithm.indexOf("/ECB") > -1))\r
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);\r
+ else\r
+ cipher.init(Cipher.DECRYPT_MODE, secretKey,ivspec);\r
+ }\r
+ else\r
+ throw StandardException.newException(SQLState.ILLEGAL_CIPHER_MODE);\r
+ } catch (InvalidKeyException ike) {\r
+\r
+ if (algorithm.startsWith("DES")) {\r
+\r
+ SecretKeyFactory skf;\r
+ if (provider == null)\r
+ skf = SecretKeyFactory.getInstance(secretKey.getAlgorithm());\r
+ else\r
+ skf = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), provider);\r
+\r
+\r
+ // Since the key may be a series of bytes generated by an arbitary means\r
+ // we need to translate it into a key suitable for the algorithm.\r
+ secretKey = skf.translateKey(new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm()));\r
+\r
+ // ECB mode does not require IV\r
+ if (mode == CipherFactory.ENCRYPT )\r
+ {\r
+ if ((algorithm.indexOf("/ECB") > -1))\r
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);\r
+ else\r
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey,ivspec);\r
+ }\r
+ else if (mode == CipherFactory.DECRYPT)\r
+ {\r
+ if ((algorithm.indexOf("/ECB") > -1))\r
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);\r
+ else\r
+ cipher.init(Cipher.DECRYPT_MODE, secretKey,ivspec);\r
+ }\r
+\r
+ }\r
+ else\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, ike);\r
+ }\r
+ cryptixKey = secretKey;\r
+\r
+ if (cipher.getIV() == null)\r
+ ivUsed = false;\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(verifyIV(iv));\r
+\r
+ return;\r
+\r
+ }\r
+ catch (InvalidKeyException ike)\r
+ {\r
+ t = ike;\r
+ }\r
+ catch (NoSuchAlgorithmException nsae)\r
+ {\r
+ throw StandardException.newException(SQLState.ENCRYPTION_NOSUCH_ALGORITHM, algorithm, JCECipherFactory.providerErrorName(provider));\r
+ }\r
+ catch (NoSuchProviderException nspe)\r
+ {\r
+ throw StandardException.newException(SQLState.ENCRYPTION_BAD_PROVIDER, JCECipherFactory.providerErrorName(provider));\r
+ }\r
+ catch (GeneralSecurityException gse)\r
+ {\r
+ t = gse;\r
+ }\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, t);\r
+\r
+ }\r
+\r
+ /**\r
+ @see CipherProvider#encrypt\r
+\r
+ @exception StandardException Standard Derby Error Policy\r
+ */\r
+ public int encrypt(byte[] cleartext, int offset, int length,\r
+ byte[] ciphertext, int outputOffset)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(mode == CipherFactory.ENCRYPT,\r
+ "calling encrypt on a decryption engine");\r
+ SanityManager.ASSERT(cleartext != null, "encrypting null cleartext");\r
+ SanityManager.ASSERT(offset >= 0, "offset < 0");\r
+ SanityManager.ASSERT(length > 0, "length <= 0");\r
+ SanityManager.ASSERT(offset+length <= cleartext.length,\r
+ "offset+length > cleartext.length");\r
+ SanityManager.ASSERT(length <= ciphertext.length-outputOffset,\r
+ "provided ciphertext buffer insufficient");\r
+ }\r
+\r
+ int retval = 0;\r
+ try\r
+ {\r
+ // this same cipher is shared across the entire raw store, make it\r
+ // MT safe\r
+ synchronized(this)\r
+ {\r
+ if( !sunjce )\r
+ {\r
+ // this code is a workaround for other providers\r
+ try\r
+ {\r
+ //ivspec = new IvParameterSpec(cipher.getIV());\r
+ if (mode == CipherFactory.ENCRYPT)\r
+ {\r
+ if (ivUsed)\r
+ cipher.init(Cipher.ENCRYPT_MODE, cryptixKey, ivspec);\r
+ else\r
+ cipher.init(Cipher.ENCRYPT_MODE,cryptixKey);\r
+ }\r
+ else if (mode == CipherFactory.DECRYPT)\r
+ {\r
+ if (ivUsed)\r
+ cipher.init(Cipher.DECRYPT_MODE, cryptixKey, ivspec);\r
+ else\r
+ cipher.init(Cipher.DECRYPT_MODE, cryptixKey);\r
+ }\r
+\r
+ }\r
+ catch (InvalidKeyException ike)\r
+ {\r
+ System.out.println("A " + ike);\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, ike);\r
+ }\r
+ }\r
+\r
+ retval = cipher.doFinal(cleartext, offset, length, ciphertext, outputOffset);\r
+ }\r
+ }\r
+ catch (IllegalStateException ise)\r
+ {\r
+ // should never happen\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT(ise);\r
+ }\r
+ catch (GeneralSecurityException gse)\r
+ {\r
+ System.out.println("B " + gse);\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, gse);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(retval == length, "ciphertext length != length");\r
+\r
+ return retval;\r
+ }\r
+\r
+\r
+ /**\r
+ @see CipherProvider#decrypt\r
+\r
+ @exception StandardException Standard Derby Error Policy\r
+ */\r
+ public int decrypt(byte[] ciphertext, int offset, int length,\r
+ byte[] cleartext, int outputOffset)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(mode == CipherFactory.DECRYPT,\r
+ "calling decrypt on a encryption engine");\r
+ SanityManager.ASSERT(ciphertext != null, "decrypting null ciphertext");\r
+ SanityManager.ASSERT(offset >= 0, "offset < 0");\r
+ SanityManager.ASSERT(length > 0, "length <= 0");\r
+ SanityManager.ASSERT(offset+length <= ciphertext.length,\r
+ "offset+length > ciphertext.length");\r
+ SanityManager.ASSERT(length <= cleartext.length-outputOffset,\r
+ "provided cleartexte buffer insufficient");\r
+ }\r
+\r
+ int retval = 0;\r
+ try\r
+ {\r
+ // this same cipher is shared across the entire raw store, make it\r
+ // MT safe\r
+ synchronized(this)\r
+ {\r
+ if( !sunjce )\r
+ {\r
+ // this code is a workaround for other providers\r
+ try\r
+ {\r
+ //ivspec = new IvParameterSpec(cipher.getIV());\r
+\r
+ if (mode == CipherFactory.ENCRYPT)\r
+ {\r
+ if (ivUsed)\r
+ cipher.init(Cipher.ENCRYPT_MODE, cryptixKey, ivspec);\r
+ else\r
+ cipher.init(Cipher.ENCRYPT_MODE,cryptixKey);\r
+ }\r
+ else if (mode == CipherFactory.DECRYPT)\r
+ {\r
+ if (ivUsed)\r
+ cipher.init(Cipher.DECRYPT_MODE, cryptixKey, ivspec);\r
+ else\r
+ cipher.init(Cipher.DECRYPT_MODE, cryptixKey);\r
+ }\r
+\r
+ }\r
+ catch (InvalidKeyException ike)\r
+ {\r
+ System.out.println("C " + ike);\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, ike);\r
+ }\r
+\r
+ }\r
+\r
+ retval = cipher.doFinal(ciphertext, offset, length, cleartext, outputOffset);\r
+ }\r
+ }\r
+ catch (IllegalStateException ise)\r
+ {\r
+ // should never happen\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT(ise);\r
+ }\r
+ catch (GeneralSecurityException gse)\r
+ {\r
+ System.out.println("D " + gse);\r
+ throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, gse);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(retval == length,\r
+ "cleartext length != length");\r
+ }\r
+\r
+ return retval;\r
+ }\r
+\r
+ boolean verifyIV(byte[] IV)\r
+ {\r
+ byte[] myIV = cipher.getIV();\r
+ // null IV is OK only if IV is not used\r
+ if (myIV == null)\r
+ return !ivUsed;\r
+ if (myIV.length != IV.length)\r
+ return false;\r
+ for (int i = 0; i < IV.length; i++)\r
+ if (myIV[i] != IV[i])\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ public int getEncryptionBlockSize()\r
+ {\r
+ return encryptionBlockSize;\r
+ }\r
+}\r