001: /*
002:
003: Derby - Class org.apache.derby.impl.services.jce.JCECipherProvider
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.services.jce;
023:
024: import org.apache.derby.iapi.services.crypto.CipherFactory;
025: import org.apache.derby.iapi.services.crypto.CipherProvider;
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.error.StandardException;
029: import org.apache.derby.iapi.reference.SQLState;
030:
031: import java.security.Key;
032: import java.security.InvalidKeyException;
033: import java.security.NoSuchAlgorithmException;
034: import java.security.GeneralSecurityException;
035: import java.security.NoSuchProviderException;
036:
037: import javax.crypto.Cipher;
038: import javax.crypto.spec.IvParameterSpec;
039: import javax.crypto.SecretKeyFactory;
040: import javax.crypto.spec.SecretKeySpec;
041: import javax.crypto.SecretKey;
042:
043: /**
044: This is a wrapper for a Cipher
045:
046: @see CipherFactory
047: */
048: class JCECipherProvider implements CipherProvider {
049: private Cipher cipher;
050: private int mode;
051: private boolean ivUsed = true;
052: private final IvParameterSpec ivspec;
053: private final int encryptionBlockSize;
054: private boolean sunjce; //default of bool is false
055:
056: // other provider workaround, we need to re-init the cipher before every encrypt/decrypt
057: private SecretKey cryptixKey;
058:
059: JCECipherProvider(int mode, SecretKey secretKey, byte[] iv,
060: String algorithm, String provider) throws StandardException {
061: Throwable t;
062: ivspec = new IvParameterSpec(iv);
063: try {
064:
065: if (provider == null) {
066: cipher = Cipher.getInstance(algorithm);
067:
068: // see below.
069: if ("SunJCE".equals(cipher.getProvider().getName()))
070: sunjce = true;
071: } else {
072: /* The Sun encryption provider does not need to re-init the cipher
073: * after each encrypt/decrypt. This is a speed up trick.
074: * Other crypto providers needs this because the encrypt/decrypt
075: * ciphers becomes out of sync after an encrypt/decrypt operation.
076: */
077: if (provider.equals("SunJCE")) {
078: sunjce = true;
079: } else {
080: /* The BouncyCastle encryption provider is named "BC".
081: * The full "BouncyCastleProvider" name used to work until
082: * version 103 came out. (ie. Beta3 and Beta4 works fine)
083: * This trick is so that Cipher.getInstance(algo, prov) will
084: * not throw an exception. Resolve 3765.
085: */
086: if (provider.equals("BouncyCastleProvider"))
087: provider = "BC";
088: }
089:
090: cipher = Cipher.getInstance(algorithm, provider);
091: }
092:
093: // At creation time, the encryption block size is stored in order
094: // to do appropriate padding
095: encryptionBlockSize = cipher.getBlockSize();
096:
097: this .mode = mode;
098: try {
099:
100: // ECB feedback mode does not require an IV
101: if (mode == CipherFactory.ENCRYPT) {
102: if ((algorithm.indexOf("/ECB") > -1))
103: cipher.init(Cipher.ENCRYPT_MODE, secretKey);
104: else
105: cipher.init(Cipher.ENCRYPT_MODE, secretKey,
106: ivspec);
107: } else if (mode == CipherFactory.DECRYPT) {
108: if ((algorithm.indexOf("/ECB") > -1))
109: cipher.init(Cipher.DECRYPT_MODE, secretKey);
110: else
111: cipher.init(Cipher.DECRYPT_MODE, secretKey,
112: ivspec);
113: } else
114: throw StandardException
115: .newException(SQLState.ILLEGAL_CIPHER_MODE);
116: } catch (InvalidKeyException ike) {
117:
118: if (algorithm.startsWith("DES")) {
119:
120: SecretKeyFactory skf;
121: if (provider == null)
122: skf = SecretKeyFactory.getInstance(secretKey
123: .getAlgorithm());
124: else
125: skf = SecretKeyFactory.getInstance(secretKey
126: .getAlgorithm(), provider);
127:
128: // Since the key may be a series of bytes generated by an arbitary means
129: // we need to translate it into a key suitable for the algorithm.
130: secretKey = skf.translateKey(new SecretKeySpec(
131: secretKey.getEncoded(), secretKey
132: .getAlgorithm()));
133:
134: // ECB mode does not require IV
135: if (mode == CipherFactory.ENCRYPT) {
136: if ((algorithm.indexOf("/ECB") > -1))
137: cipher.init(Cipher.ENCRYPT_MODE, secretKey);
138: else
139: cipher.init(Cipher.ENCRYPT_MODE, secretKey,
140: ivspec);
141: } else if (mode == CipherFactory.DECRYPT) {
142: if ((algorithm.indexOf("/ECB") > -1))
143: cipher.init(Cipher.DECRYPT_MODE, secretKey);
144: else
145: cipher.init(Cipher.DECRYPT_MODE, secretKey,
146: ivspec);
147: }
148:
149: } else
150: throw StandardException.newException(
151: SQLState.CRYPTO_EXCEPTION, ike);
152: }
153: cryptixKey = secretKey;
154:
155: if (cipher.getIV() == null)
156: ivUsed = false;
157:
158: if (SanityManager.DEBUG)
159: SanityManager.ASSERT(verifyIV(iv));
160:
161: return;
162:
163: } catch (InvalidKeyException ike) {
164: t = ike;
165: } catch (NoSuchAlgorithmException nsae) {
166: throw StandardException.newException(
167: SQLState.ENCRYPTION_NOSUCH_ALGORITHM, algorithm,
168: JCECipherFactory.providerErrorName(provider));
169: } catch (NoSuchProviderException nspe) {
170: throw StandardException.newException(
171: SQLState.ENCRYPTION_BAD_PROVIDER, JCECipherFactory
172: .providerErrorName(provider));
173: } catch (GeneralSecurityException gse) {
174: t = gse;
175: }
176: throw StandardException.newException(SQLState.CRYPTO_EXCEPTION,
177: t);
178:
179: }
180:
181: /**
182: @see CipherProvider#encrypt
183:
184: @exception StandardException Standard Cloudscape Error Policy
185: */
186: public int encrypt(byte[] cleartext, int offset, int length,
187: byte[] ciphertext, int outputOffset)
188: throws StandardException {
189: if (SanityManager.DEBUG) {
190: SanityManager.ASSERT(mode == CipherFactory.ENCRYPT,
191: "calling encrypt on a decryption engine");
192: SanityManager.ASSERT(cleartext != null,
193: "encrypting null cleartext");
194: SanityManager.ASSERT(offset >= 0, "offset < 0");
195: SanityManager.ASSERT(length > 0, "length <= 0");
196: SanityManager.ASSERT(offset + length <= cleartext.length,
197: "offset+length > cleartext.length");
198: SanityManager.ASSERT(length <= ciphertext.length
199: - outputOffset,
200: "provided ciphertext buffer insufficient");
201: }
202:
203: int retval = 0;
204: try {
205: // this same cipher is shared across the entire raw store, make it
206: // MT safe
207: synchronized (this ) {
208: if (!sunjce) {
209: // this code is a workaround for other providers
210: try {
211: //ivspec = new IvParameterSpec(cipher.getIV());
212: if (mode == CipherFactory.ENCRYPT) {
213: if (ivUsed)
214: cipher.init(Cipher.ENCRYPT_MODE,
215: cryptixKey, ivspec);
216: else
217: cipher.init(Cipher.ENCRYPT_MODE,
218: cryptixKey);
219: } else if (mode == CipherFactory.DECRYPT) {
220: if (ivUsed)
221: cipher.init(Cipher.DECRYPT_MODE,
222: cryptixKey, ivspec);
223: else
224: cipher.init(Cipher.DECRYPT_MODE,
225: cryptixKey);
226: }
227:
228: } catch (InvalidKeyException ike) {
229: System.out.println("A " + ike);
230: throw StandardException.newException(
231: SQLState.CRYPTO_EXCEPTION, ike);
232: }
233: }
234:
235: retval = cipher.doFinal(cleartext, offset, length,
236: ciphertext, outputOffset);
237: }
238: } catch (IllegalStateException ise) {
239: // should never happen
240: if (SanityManager.DEBUG)
241: SanityManager.THROWASSERT("Illegal state exception");
242: } catch (GeneralSecurityException gse) {
243: System.out.println("B " + gse);
244: throw StandardException.newException(
245: SQLState.CRYPTO_EXCEPTION, gse);
246: }
247:
248: if (SanityManager.DEBUG)
249: SanityManager.ASSERT(retval == length,
250: "ciphertext length != length");
251:
252: return retval;
253: }
254:
255: /**
256: @see CipherProvider#decrypt
257:
258: @exception StandardException Standard Cloudscape Error Policy
259: */
260: public int decrypt(byte[] ciphertext, int offset, int length,
261: byte[] cleartext, int outputOffset)
262: throws StandardException {
263: if (SanityManager.DEBUG) {
264: SanityManager.ASSERT(mode == CipherFactory.DECRYPT,
265: "calling decrypt on a encryption engine");
266: SanityManager.ASSERT(ciphertext != null,
267: "decrypting null ciphertext");
268: SanityManager.ASSERT(offset >= 0, "offset < 0");
269: SanityManager.ASSERT(length > 0, "length <= 0");
270: SanityManager.ASSERT(offset + length <= ciphertext.length,
271: "offset+length > ciphertext.length");
272: SanityManager.ASSERT(length <= cleartext.length
273: - outputOffset,
274: "provided cleartexte buffer insufficient");
275: }
276:
277: int retval = 0;
278: try {
279: // this same cipher is shared across the entire raw store, make it
280: // MT safe
281: synchronized (this ) {
282: if (!sunjce) {
283: // this code is a workaround for other providers
284: try {
285: //ivspec = new IvParameterSpec(cipher.getIV());
286:
287: if (mode == CipherFactory.ENCRYPT) {
288: if (ivUsed)
289: cipher.init(Cipher.ENCRYPT_MODE,
290: cryptixKey, ivspec);
291: else
292: cipher.init(Cipher.ENCRYPT_MODE,
293: cryptixKey);
294: } else if (mode == CipherFactory.DECRYPT) {
295: if (ivUsed)
296: cipher.init(Cipher.DECRYPT_MODE,
297: cryptixKey, ivspec);
298: else
299: cipher.init(Cipher.DECRYPT_MODE,
300: cryptixKey);
301: }
302:
303: } catch (InvalidKeyException ike) {
304: System.out.println("C " + ike);
305: throw StandardException.newException(
306: SQLState.CRYPTO_EXCEPTION, ike);
307: }
308:
309: }
310:
311: retval = cipher.doFinal(ciphertext, offset, length,
312: cleartext, outputOffset);
313: }
314: } catch (IllegalStateException ise) {
315: // should never happen
316: if (SanityManager.DEBUG)
317: SanityManager.THROWASSERT("Illegal state exception");
318: } catch (GeneralSecurityException gse) {
319: System.out.println("D " + gse);
320: throw StandardException.newException(
321: SQLState.CRYPTO_EXCEPTION, gse);
322: }
323:
324: if (SanityManager.DEBUG) {
325: SanityManager.ASSERT(retval == length,
326: "cleartext length != length");
327: }
328:
329: return retval;
330: }
331:
332: boolean verifyIV(byte[] IV) {
333: byte[] myIV = cipher.getIV();
334: // null IV is OK only if IV is not used
335: if (myIV == null)
336: return !ivUsed;
337: if (myIV.length != IV.length)
338: return false;
339: for (int i = 0; i < IV.length; i++)
340: if (myIV[i] != IV[i])
341: return false;
342: return true;
343: }
344:
345: public int getEncryptionBlockSize() {
346: return encryptionBlockSize;
347: }
348: }
|