Source Code Cross Referenced for JCECipherFactory.java in  » Database-DBMS » db-derby-10.2 » org » apache » derby » impl » services » jce » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » db derby 10.2 » org.apache.derby.impl.services.jce 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:           Derby - Class org.apache.derby.impl.services.jce.JCECipherFactory
0004:
0005:           Licensed to the Apache Software Foundation (ASF) under one or more
0006:           contributor license agreements.  See the NOTICE file distributed with
0007:           this work for additional information regarding copyright ownership.
0008:           The ASF licenses this file to you under the Apache License, Version 2.0
0009:           (the "License"); you may not use this file except in compliance with
0010:           the License.  You may obtain a copy of the License at
0011:
0012:              http://www.apache.org/licenses/LICENSE-2.0
0013:
0014:           Unless required by applicable law or agreed to in writing, software
0015:           distributed under the License is distributed on an "AS IS" BASIS,
0016:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017:           See the License for the specific language governing permissions and
0018:           limitations under the License.
0019:
0020:         */
0021:
0022:        package org.apache.derby.impl.services.jce;
0023:
0024:        import org.apache.derby.iapi.services.crypto.CipherFactory;
0025:        import org.apache.derby.iapi.services.crypto.CipherProvider;
0026:
0027:        import org.apache.derby.iapi.services.monitor.Monitor;
0028:        import org.apache.derby.iapi.services.sanity.SanityManager;
0029:
0030:        import org.apache.derby.iapi.error.StandardException;
0031:
0032:        import org.apache.derby.iapi.services.info.JVMInfo;
0033:        import org.apache.derby.iapi.util.StringUtil;
0034:
0035:        import org.apache.derby.iapi.reference.SQLState;
0036:        import org.apache.derby.iapi.reference.Attribute;
0037:        import org.apache.derby.iapi.util.StringUtil;
0038:
0039:        import java.util.Properties;
0040:        import java.util.Enumeration;
0041:        import java.security.Key;
0042:        import java.security.Provider;
0043:        import java.security.SecureRandom;
0044:        import java.security.Security;
0045:        import java.security.InvalidKeyException;
0046:        import java.security.NoSuchAlgorithmException;
0047:        import java.security.MessageDigest;
0048:        import java.security.spec.KeySpec;
0049:        import java.security.spec.InvalidKeySpecException;
0050:        import java.io.FileNotFoundException;
0051:        import java.io.IOException;
0052:        import java.io.InputStream;
0053:        import java.io.DataInputStream;
0054:
0055:        import javax.crypto.KeyGenerator;
0056:        import javax.crypto.SecretKey;
0057:        import javax.crypto.SecretKeyFactory;
0058:        import javax.crypto.spec.DESKeySpec;
0059:        import javax.crypto.spec.SecretKeySpec;
0060:        import org.apache.derby.iapi.store.raw.RawStoreFactory;
0061:
0062:        import org.apache.derby.io.StorageFactory;
0063:        import org.apache.derby.io.WritableStorageFactory;
0064:        import org.apache.derby.io.StorageFile;
0065:        import org.apache.derby.io.StorageRandomAccessFile;
0066:
0067:        /**
0068:         This CipherFactory creates new JCECipherProvider.
0069:
0070:         @see CipherFactory
0071:         */
0072:        public final class JCECipherFactory implements  CipherFactory,
0073:                java.security.PrivilegedExceptionAction {
0074:            private final static String MESSAGE_DIGEST = "MD5";
0075:
0076:            private final static String DEFAULT_PROVIDER = "com.sun.crypto.provider.SunJCE";
0077:            private final static String DEFAULT_ALGORITHM = "DES/CBC/NoPadding";
0078:            private final static String DES = "DES";
0079:            private final static String DESede = "DESede";
0080:            private final static String TripleDES = "TripleDES";
0081:            private final static String AES = "AES";
0082:
0083:            // minimum boot password length in bytes
0084:            private final static int BLOCK_LENGTH = 8;
0085:
0086:            /**
0087:            AES encryption takes in an default Initialization vector length (IV) length of 16 bytes
0088:            This is needed to generate an IV to use for encryption and decryption process 
0089:            @see CipherProvider
0090:             */
0091:            private final static int AES_IV_LENGTH = 16;
0092:
0093:            // key length in bytes
0094:            private int keyLengthBits;
0095:            private int encodedKeyLength;
0096:            private String cryptoAlgorithm;
0097:            private String cryptoAlgorithmShort;
0098:            private String cryptoProvider;
0099:            private String cryptoProviderShort;
0100:            private MessageDigest messageDigest;
0101:
0102:            private SecretKey mainSecretKey;
0103:            private byte[] mainIV;
0104:
0105:            // properties that needs to be stored in the
0106:            // in the service.properties file.
0107:            private Properties persistentProperties;
0108:
0109:            /**
0110:                Amount of data that is used for verification of external encryption key
0111:                This does not include the MD5 checksum bytes
0112:             */
0113:            private final static int VERIFYKEY_DATALEN = 4096;
0114:            private StorageFile activeFile;
0115:            private int action;
0116:            private String activePerms;
0117:
0118:            /*
0119:             * Constructor of JCECipherFactory, initializes the new instances.
0120:             *
0121:             * @param create    true, if the database is getting configured 
0122:             *                  for encryption.
0123:             * @param props	    encryption properties/attributes to use
0124:             *                  for creating the cipher factory.
0125:             * @param newAttrs  true, if cipher factory has to be created using 
0126:             *                  should using the new attributes specified by the user.  
0127:             *                  For example to reencrypt the database with 
0128:             *                  a new password.
0129:             */
0130:            public JCECipherFactory(boolean create, Properties props,
0131:                    boolean newAttributes) throws StandardException {
0132:                init(create, props, newAttributes);
0133:            }
0134:
0135:            static String providerErrorName(String cps) {
0136:
0137:                return cps == null ? "default" : cps;
0138:            }
0139:
0140:            private byte[] generateUniqueBytes() throws StandardException {
0141:                try {
0142:
0143:                    String provider = cryptoProviderShort;
0144:
0145:                    KeyGenerator keyGen;
0146:                    if (provider == null) {
0147:                        keyGen = KeyGenerator.getInstance(cryptoAlgorithmShort);
0148:                    } else {
0149:                        if (provider.equals("BouncyCastleProvider"))
0150:                            provider = "BC";
0151:                        keyGen = KeyGenerator.getInstance(cryptoAlgorithmShort,
0152:                                provider);
0153:                    }
0154:
0155:                    keyGen.init(keyLengthBits);
0156:
0157:                    SecretKey key = keyGen.generateKey();
0158:
0159:                    return key.getEncoded();
0160:
0161:                } catch (java.security.NoSuchAlgorithmException nsae) {
0162:                    throw StandardException.newException(
0163:                            SQLState.ENCRYPTION_NOSUCH_ALGORITHM,
0164:                            cryptoAlgorithm, JCECipherFactory
0165:                                    .providerErrorName(cryptoProviderShort));
0166:                } catch (java.security.NoSuchProviderException nspe) {
0167:                    throw StandardException.newException(
0168:                            SQLState.ENCRYPTION_BAD_PROVIDER, JCECipherFactory
0169:                                    .providerErrorName(cryptoProviderShort));
0170:                }
0171:            }
0172:
0173:            /**
0174:            	Encrypt the secretKey with the boot password.
0175:            	This includes the following steps, 
0176:            	getting muck from the boot password and then using this to generate a key,
0177:            	generating an appropriate IV using the muck
0178:            	using the key and IV thus generated to create the appropriate cipher provider
0179:            	and encrypting the secretKey 
0180:            	@return hexadecimal string of the encrypted secretKey
0181:
0182:            	@exception StandardException Standard Cloudscape error policy
0183:             */
0184:            private String encryptKey(byte[] secretKey, byte[] bootPassword)
0185:                    throws StandardException {
0186:                // In case of AES, care needs to be taken to allow for 16 bytes muck as well
0187:                // as to have the secretKey that needs encryption to be a aligned appropriately
0188:                // AES supports 16 bytes block size
0189:
0190:                int muckLength = secretKey.length;
0191:                if (cryptoAlgorithmShort.equals(AES))
0192:                    muckLength = AES_IV_LENGTH;
0193:
0194:                byte[] muck = getMuckFromBootPassword(bootPassword, muckLength);
0195:                SecretKey key = generateKey(muck);
0196:                byte[] IV = generateIV(muck);
0197:                CipherProvider tmpCipherProvider = createNewCipher(ENCRYPT,
0198:                        key, IV);
0199:
0200:                // store the actual secretKey.length before any possible padding  
0201:                encodedKeyLength = secretKey.length;
0202:
0203:                // for the secretKey to be encrypted, first ensure that it is aligned to the block size of the 
0204:                // encryption algorithm by padding bytes appropriately if needed
0205:                secretKey = padKey(secretKey, tmpCipherProvider
0206:                        .getEncryptionBlockSize());
0207:
0208:                byte[] result = new byte[secretKey.length];
0209:
0210:                // encrypt the secretKey using the key generated of muck from  boot password and the generated IV  
0211:                tmpCipherProvider.encrypt(secretKey, 0, secretKey.length,
0212:                        result, 0);
0213:
0214:                return org.apache.derby.iapi.util.StringUtil.toHexString(
0215:                        result, 0, result.length);
0216:
0217:            }
0218:
0219:            /**
0220:                    For block ciphers, and  algorithms using the NoPadding scheme, the data that has 
0221:                    to be encrypted needs to be a multiple of the expected block size for the cipher 
0222:                Pad the key with appropriate padding to make it blockSize align
0223:                @param     secretKey	the data that needs blocksize alignment
0224:                @param     blockSizeAlign   secretKey needs to be blocksize aligned		
0225:                @return    a byte array with the contents of secretKey along with padded bytes in the end
0226:            	       to make it blockSize aligned
0227:             */
0228:            private byte[] padKey(byte[] secretKey, int blockSizeAlign) {
0229:                byte[] result = secretKey;
0230:                if (secretKey.length % blockSizeAlign != 0) {
0231:                    int encryptedLength = secretKey.length + blockSizeAlign
0232:                            - (secretKey.length % blockSizeAlign);
0233:                    result = new byte[encryptedLength];
0234:                    System.arraycopy(secretKey, 0, result, 0, secretKey.length);
0235:                }
0236:                return result;
0237:            }
0238:
0239:            /**
0240:                Decrypt the secretKey with the user key .
0241:                This includes the following steps, 
0242:                retrieve the encryptedKey, generate the muck from the boot password and generate an appropriate IV using
0243:                the muck,and using the key and IV decrypt the encryptedKey 
0244:                @return decrypted key  
0245:            	@exception StandardException Standard Cloudscape error policy
0246:             */
0247:            private byte[] decryptKey(String encryptedKey,
0248:                    int encodedKeyCharLength, byte[] bootPassword)
0249:                    throws StandardException {
0250:                byte[] secretKey = org.apache.derby.iapi.util.StringUtil
0251:                        .fromHexString(encryptedKey, 0, encodedKeyCharLength);
0252:                // In case of AES, care needs to be taken to allow for 16 bytes muck as well
0253:                // as to have the secretKey that needs encryption to be a aligned appropriately
0254:                // AES supports 16 bytes block size
0255:                int muckLength;
0256:                if (cryptoAlgorithmShort.equals(AES))
0257:                    muckLength = AES_IV_LENGTH;
0258:                else
0259:                    muckLength = secretKey.length;
0260:
0261:                byte[] muck = getMuckFromBootPassword(bootPassword, muckLength);
0262:
0263:                // decrypt the encryptedKey with the mucked up boot password to recover
0264:                // the secretKey
0265:                SecretKey key = generateKey(muck);
0266:                byte[] IV = generateIV(muck);
0267:
0268:                createNewCipher(DECRYPT, key, IV).decrypt(secretKey, 0,
0269:                        secretKey.length, secretKey, 0);
0270:
0271:                return secretKey;
0272:            }
0273:
0274:            private byte[] getMuckFromBootPassword(byte[] bootPassword,
0275:                    int encodedKeyByteLength) {
0276:                int ulength = bootPassword.length;
0277:
0278:                byte[] muck = new byte[encodedKeyByteLength];
0279:
0280:                int rotation = 0;
0281:                for (int i = 0; i < bootPassword.length; i++)
0282:                    rotation += bootPassword[i];
0283:
0284:                for (int i = 0; i < encodedKeyByteLength; i++)
0285:                    muck[i] = (byte) (bootPassword[(i + rotation) % ulength] ^ (bootPassword[i
0286:                            % ulength] << 4));
0287:
0288:                return muck;
0289:            }
0290:
0291:            /**
0292:            	Generate a Key object using the input secretKey that can be used by
0293:            	JCECipherProvider to encrypt or decrypt.
0294:
0295:            	@exception StandardException Standard Cloudscape Error Policy
0296:             */
0297:            private SecretKey generateKey(byte[] secretKey)
0298:                    throws StandardException {
0299:                int length = secretKey.length;
0300:
0301:                if (length < CipherFactory.MIN_BOOTPASS_LENGTH)
0302:                    throw StandardException.newException(
0303:                            SQLState.ILLEGAL_BP_LENGTH, new Integer(
0304:                                    MIN_BOOTPASS_LENGTH));
0305:
0306:                try {
0307:                    if (cryptoAlgorithmShort.equals(DES)) { // single DES
0308:                        if (DESKeySpec.isWeak(secretKey, 0)) {
0309:                            // OK, it is weak, spice it up
0310:                            byte[] spice = StringUtil.getAsciiBytes("louDScap");
0311:                            for (int i = 0; i < 7; i++)
0312:                                secretKey[i] = (byte) ((spice[i] << 3) ^ secretKey[i]);
0313:                        }
0314:                    }
0315:                    return new SecretKeySpec(secretKey, cryptoAlgorithmShort);
0316:                } catch (InvalidKeyException ike) {
0317:                    throw StandardException.newException(
0318:                            SQLState.CRYPTO_EXCEPTION, ike);
0319:                }
0320:
0321:            }
0322:
0323:            /**
0324:            	Generate an IV using the input secretKey that can be used by
0325:            	JCECipherProvider to encrypt or decrypt.
0326:             */
0327:            private byte[] generateIV(byte[] secretKey) {
0328:
0329:                // do a little simple minded muddling to make the IV not
0330:                // strictly alphanumeric and the number of total possible keys a little
0331:                // bigger.
0332:                int IVlen = BLOCK_LENGTH;
0333:
0334:                byte[] iv = null;
0335:                if (cryptoAlgorithmShort.equals(AES)) {
0336:                    IVlen = AES_IV_LENGTH;
0337:                    iv = new byte[IVlen];
0338:                    iv[0] = (byte) (((secretKey[secretKey.length - 1] << 2) | 0xF) ^ secretKey[0]);
0339:                    for (int i = 1; i < BLOCK_LENGTH; i++)
0340:                        iv[i] = (byte) (((secretKey[i - 1] << (i % 5)) | 0xF) ^ secretKey[i]);
0341:
0342:                    for (int i = BLOCK_LENGTH; i < AES_IV_LENGTH; i++) {
0343:                        iv[i] = iv[i - BLOCK_LENGTH];
0344:                    }
0345:
0346:                } else {
0347:                    iv = new byte[BLOCK_LENGTH];
0348:                    iv[0] = (byte) (((secretKey[secretKey.length - 1] << 2) | 0xF) ^ secretKey[0]);
0349:                    for (int i = 1; i < BLOCK_LENGTH; i++)
0350:                        iv[i] = (byte) (((secretKey[i - 1] << (i % 5)) | 0xF) ^ secretKey[i]);
0351:                }
0352:
0353:                return iv;
0354:            }
0355:
0356:            private int digest(byte[] input) {
0357:                messageDigest.reset();
0358:                byte[] digest = messageDigest.digest(input);
0359:                byte[] condenseDigest = new byte[2];
0360:
0361:                // no matter how long the digest is, condense it into an short.
0362:                for (int i = 0; i < digest.length; i++)
0363:                    condenseDigest[i % 2] ^= digest[i];
0364:
0365:                int retval = (condenseDigest[0] & 0xFF)
0366:                        | ((condenseDigest[1] << 8) & 0xFF00);
0367:
0368:                return retval;
0369:            }
0370:
0371:            public SecureRandom getSecureRandom() {
0372:                return new SecureRandom(mainIV);
0373:            }
0374:
0375:            public CipherProvider createNewCipher(int mode)
0376:                    throws StandardException {
0377:                return createNewCipher(mode, mainSecretKey, mainIV);
0378:            }
0379:
0380:            private CipherProvider createNewCipher(int mode,
0381:                    SecretKey secretKey, byte[] iv) throws StandardException {
0382:                return new JCECipherProvider(mode, secretKey, iv,
0383:                        cryptoAlgorithm, cryptoProviderShort);
0384:            }
0385:
0386:            /*
0387:             * Initilize the new instance of this class. 
0388:             */
0389:            public void init(boolean create, Properties properties,
0390:                    boolean newAttrs) throws StandardException {
0391:
0392:                boolean provider_or_algo_specified = false;
0393:                boolean storeProperties = create;
0394:                persistentProperties = new Properties();
0395:
0396:                // get the external key specified by the user to 
0397:                // encrypt the database. If user is reencrypting the
0398:                // database with a new encryption key,  read the value of 
0399:                // the new encryption key. 
0400:                String externalKey = properties
0401:                        .getProperty((newAttrs ? Attribute.NEW_CRYPTO_EXTERNAL_KEY
0402:                                : Attribute.CRYPTO_EXTERNAL_KEY));
0403:                if (externalKey != null) {
0404:                    storeProperties = false;
0405:                }
0406:
0407:                cryptoProvider = properties
0408:                        .getProperty(Attribute.CRYPTO_PROVIDER);
0409:
0410:                if (cryptoProvider == null) {
0411:                    // JDK 1.3 does not create providers by itself.
0412:                    if (JVMInfo.JDK_ID == JVMInfo.J2SE_13) {
0413:
0414:                        String vendor;
0415:                        try {
0416:                            vendor = System.getProperty("java.vendor", "");
0417:                        } catch (SecurityException se) {
0418:                            vendor = "";
0419:                        }
0420:
0421:                        vendor = StringUtil.SQLToUpperCase(vendor);
0422:
0423:                        if (vendor.startsWith("IBM "))
0424:                            cryptoProvider = "com.ibm.crypto.provider.IBMJCE";
0425:                        else if (vendor.startsWith("SUN "))
0426:                            cryptoProvider = "com.sun.crypto.provider.SunJCE";
0427:
0428:                    }
0429:                } else {
0430:                    provider_or_algo_specified = true;
0431:
0432:                    // explictly putting the properties back into the properties
0433:                    // saves then in service.properties at create time.
0434:                    //	if (storeProperties)
0435:                    //		properties.put(Attribute.CRYPTO_PROVIDER, cryptoProvider);
0436:
0437:                    int dotPos = cryptoProvider.lastIndexOf('.');
0438:                    if (dotPos == -1)
0439:                        cryptoProviderShort = cryptoProvider;
0440:                    else
0441:                        cryptoProviderShort = cryptoProvider
0442:                                .substring(dotPos + 1);
0443:
0444:                }
0445:
0446:                cryptoAlgorithm = properties
0447:                        .getProperty(Attribute.CRYPTO_ALGORITHM);
0448:                if (cryptoAlgorithm == null)
0449:                    cryptoAlgorithm = DEFAULT_ALGORITHM;
0450:                else {
0451:                    provider_or_algo_specified = true;
0452:
0453:                }
0454:
0455:                // explictly putting the properties back into the properties
0456:                // saves then in service.properties at create time.
0457:                if (storeProperties)
0458:                    persistentProperties.put(Attribute.CRYPTO_ALGORITHM,
0459:                            cryptoAlgorithm);
0460:
0461:                int firstSlashPos = cryptoAlgorithm.indexOf('/');
0462:                int lastSlashPos = cryptoAlgorithm.lastIndexOf('/');
0463:                if (firstSlashPos < 0 || lastSlashPos < 0
0464:                        || firstSlashPos == lastSlashPos)
0465:                    throw StandardException
0466:                            .newException(SQLState.ENCRYPTION_BAD_ALG_FORMAT,
0467:                                    cryptoAlgorithm);
0468:
0469:                cryptoAlgorithmShort = cryptoAlgorithm.substring(0,
0470:                        firstSlashPos);
0471:
0472:                if (provider_or_algo_specified) {
0473:                    // Track 3715 - disable use of provider/aglo specification if
0474:                    // jce environment is not 1.2.1.  The ExemptionMechanism class
0475:                    // exists in jce1.2.1 and not in jce1.2, so try and load the
0476:                    // class and if you can't find it don't allow the encryption.
0477:                    // This is a requirement from the government to give cloudscape
0478:                    // export clearance for 3.6.  Note that the check is not needed
0479:                    // if no provider/algo is specified, in that case we default to
0480:                    // a DES weak encryption algorithm which also is allowed for
0481:                    // export (this is how 3.5 got it's clearance).
0482:                    try {
0483:                        Class c = Class
0484:                                .forName("javax.crypto.ExemptionMechanism");
0485:                    } catch (Throwable t) {
0486:                        throw StandardException
0487:                                .newException(SQLState.ENCRYPTION_BAD_JCE);
0488:                    }
0489:                }
0490:
0491:                // If connecting to an existing database and Attribute.CRYPTO_KEY_LENGTH is set
0492:                // then obtain the encoded key length values without padding bytes and retrieve
0493:                // the keylength in bits if boot password mechanism is used 
0494:                // note: Attribute.CRYPTO_KEY_LENGTH is set during creation time to a supported
0495:                // key length in the connection url. Internally , two values are stored in this property
0496:                // if encryptionKey is used, this property will have only the encoded key length
0497:                // if boot password mechanism is used, this property will have the following 
0498:                // keylengthBits-EncodedKeyLength 
0499:
0500:                if (!create) {
0501:                    // if available, parse the keylengths stored in Attribute.CRYPTO_KEY_LENGTH 
0502:                    if (properties.getProperty(Attribute.CRYPTO_KEY_LENGTH) != null) {
0503:                        String keyLengths = properties
0504:                                .getProperty(Attribute.CRYPTO_KEY_LENGTH);
0505:                        int pos = keyLengths.lastIndexOf('-');
0506:                        encodedKeyLength = Integer.parseInt(keyLengths
0507:                                .substring(pos + 1));
0508:                        if (pos != -1)
0509:                            keyLengthBits = Integer.parseInt(keyLengths
0510:                                    .substring(0, pos));
0511:                    }
0512:                }
0513:
0514:                // case 1 - if 'encryptionKey' is not set and 'encryptionKeyLength' is set, then use
0515:                // the 'encryptionKeyLength' property value  as the keyLength in bits.
0516:                // case 2 - 'encryptionKey' property is not set and 'encryptionKeyLength' is not set, then
0517:                // use the defaults keylength:  56bits for DES, 168 for DESede and 128 for any other encryption
0518:                // algorithm
0519:
0520:                if (externalKey == null && create) {
0521:                    if (properties.getProperty(Attribute.CRYPTO_KEY_LENGTH) != null) {
0522:                        keyLengthBits = Integer.parseInt(properties
0523:                                .getProperty(Attribute.CRYPTO_KEY_LENGTH));
0524:                    } else if (cryptoAlgorithmShort.equals(DES)) {
0525:                        keyLengthBits = 56;
0526:                    } else if (cryptoAlgorithmShort.equals(DESede)
0527:                            || cryptoAlgorithmShort.equals(TripleDES)) {
0528:                        keyLengthBits = 168;
0529:
0530:                    } else {
0531:                        keyLengthBits = 128;
0532:                    }
0533:                }
0534:
0535:                // check the feedback mode
0536:                String feedbackMode = cryptoAlgorithm.substring(
0537:                        firstSlashPos + 1, lastSlashPos);
0538:
0539:                if (!feedbackMode.equals("CBC") && !feedbackMode.equals("CFB")
0540:                        && !feedbackMode.equals("ECB")
0541:                        && !feedbackMode.equals("OFB"))
0542:                    throw StandardException.newException(
0543:                            SQLState.ENCRYPTION_BAD_FEEDBACKMODE, feedbackMode);
0544:
0545:                // check the NoPadding mode is used
0546:                String padding = cryptoAlgorithm.substring(lastSlashPos + 1,
0547:                        cryptoAlgorithm.length());
0548:                if (!padding.equals("NoPadding"))
0549:                    throw StandardException.newException(
0550:                            SQLState.ENCRYPTION_BAD_PADDING, padding);
0551:
0552:                Throwable t;
0553:                try {
0554:                    if (cryptoProvider != null) {
0555:                        // provider package should be set by property
0556:                        if (Security.getProvider(cryptoProviderShort) == null) {
0557:                            action = 1;
0558:                            // add provider through privileged block.
0559:                            java.security.AccessController.doPrivileged(this );
0560:                        }
0561:                    }
0562:
0563:                    // need this to check the boot password
0564:                    messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST);
0565:
0566:                    byte[] generatedKey;
0567:                    if (externalKey != null) {
0568:
0569:                        // incorrect to specify external key and boot password
0570:                        if (properties
0571:                                .getProperty((newAttrs ? Attribute.NEW_BOOT_PASSWORD
0572:                                        : Attribute.BOOT_PASSWORD)) != null)
0573:                            throw StandardException
0574:                                    .newException(SQLState.SERVICE_WRONG_BOOT_PASSWORD);
0575:
0576:                        generatedKey = org.apache.derby.iapi.util.StringUtil
0577:                                .fromHexString(externalKey, 0, externalKey
0578:                                        .length());
0579:                        if (generatedKey == null) {
0580:                            throw StandardException.newException(
0581:                            // If length is even, we assume invalid character(s),
0582:                                    // based on how 'fromHexString' behaves.
0583:                                    externalKey.length() % 2 == 0 ? SQLState.ENCRYPTION_ILLEGAL_EXKEY_CHARS
0584:                                            : SQLState.ENCRYPTION_INVALID_EXKEY_LENGTH);
0585:                        }
0586:
0587:                    } else {
0588:
0589:                        generatedKey = handleBootPassword(create, properties,
0590:                                newAttrs);
0591:                        if (create || newAttrs)
0592:                            persistentProperties.put(
0593:                                    Attribute.CRYPTO_KEY_LENGTH, keyLengthBits
0594:                                            + "-" + generatedKey.length);
0595:                    }
0596:
0597:                    // Make a key and IV object out of the generated key
0598:                    mainSecretKey = generateKey(generatedKey);
0599:                    mainIV = generateIV(generatedKey);
0600:
0601:                    if (create) {
0602:                        persistentProperties.put(Attribute.DATA_ENCRYPTION,
0603:                                "true");
0604:
0605:                        // Set two new properties to allow for future changes to the log and data encryption
0606:                        // schemes. This property is introduced in version 10 , value starts at 1.
0607:                        persistentProperties.put(
0608:                                RawStoreFactory.DATA_ENCRYPT_ALGORITHM_VERSION,
0609:                                String.valueOf(1));
0610:                        persistentProperties.put(
0611:                                RawStoreFactory.LOG_ENCRYPT_ALGORITHM_VERSION,
0612:                                String.valueOf(1));
0613:                    }
0614:
0615:                    return;
0616:                } catch (java.security.PrivilegedActionException pae) {
0617:                    t = pae.getException();
0618:                } catch (NoSuchAlgorithmException nsae) {
0619:                    t = nsae;
0620:                } catch (SecurityException se) {
0621:                    t = se;
0622:                } catch (LinkageError le) {
0623:                    t = le;
0624:                } catch (ClassCastException cce) {
0625:                    t = cce;
0626:                }
0627:
0628:                throw StandardException.newException(
0629:                        SQLState.MISSING_ENCRYPTION_PROVIDER, t);
0630:            }
0631:
0632:            private byte[] handleBootPassword(boolean create,
0633:                    Properties properties, boolean newPasswd)
0634:                    throws StandardException {
0635:
0636:                // get the key  specifed by the user. If user is reencrypting the
0637:                // database; read the value of the new password. 
0638:                String inputKey = properties
0639:                        .getProperty((newPasswd ? Attribute.NEW_BOOT_PASSWORD
0640:                                : Attribute.BOOT_PASSWORD));
0641:                if (inputKey == null) {
0642:                    throw StandardException
0643:                            .newException(SQLState.SERVICE_WRONG_BOOT_PASSWORD);
0644:                }
0645:
0646:                byte[] bootPassword = StringUtil.getAsciiBytes(inputKey);
0647:
0648:                if (bootPassword.length < CipherFactory.MIN_BOOTPASS_LENGTH) {
0649:                    String messageId = create ? SQLState.SERVICE_BOOT_PASSWORD_TOO_SHORT
0650:                            : SQLState.SERVICE_WRONG_BOOT_PASSWORD;
0651:
0652:                    throw StandardException.newException(messageId);
0653:                }
0654:
0655:                // Each database has its own unique encryption key that is
0656:                // not known even to the user.  However, this key is masked
0657:                // with the user input key and stored in the
0658:                // services.properties file so that, with the user key, the
0659:                // encryption key can easily be recovered.
0660:                // To change the user encryption key to a database, simply
0661:                // recover the unique real encryption key and masked it
0662:                // with the new user key.
0663:
0664:                byte[] generatedKey;
0665:
0666:                if (create || newPasswd) {
0667:                    //
0668:                    generatedKey = generateUniqueBytes();
0669:
0670:                    persistentProperties.put(RawStoreFactory.ENCRYPTED_KEY,
0671:                            saveSecretKey(generatedKey, bootPassword));
0672:
0673:                } else {
0674:                    generatedKey = getDatabaseSecretKey(properties,
0675:                            bootPassword, SQLState.SERVICE_WRONG_BOOT_PASSWORD);
0676:                }
0677:
0678:                return generatedKey;
0679:            }
0680:
0681:            /* 
0682:             * put all the encyrpion cipger related properties that has to 
0683:             * be made peristent into the database service properties list.
0684:             * @param  properties  properties object that is used to store 
0685:             *                     cipher properties persistently. 
0686:             */
0687:            public void saveProperties(Properties properties) {
0688:                // put the cipher properties to be persistent into the 
0689:                // system perisistent properties. 
0690:                for (Enumeration e = persistentProperties.keys(); e
0691:                        .hasMoreElements();) {
0692:                    String key = (String) e.nextElement();
0693:                    properties.put(key, persistentProperties.get(key));
0694:                }
0695:
0696:                // clear the cipher properties to be persistent. 
0697:                persistentProperties = null;
0698:            }
0699:
0700:            /**
0701:            	get the secretkey used for encryption and decryption when boot password mechanism is used for encryption
0702:            	Steps include 
0703:            	retrieve the stored key, decrypt the stored key and verify if the correct boot password was passed 
0704:            	There is a possibility that the decrypted key includes the original key and padded bytes in order to have
0705:            	been block size aligned during encryption phase. Hence extract the original key 
0706:            	
0707:            	@param	properties	properties to retrieve the encrypted key  
0708:            	@param	bootPassword	boot password used to connect to the encrypted database
0709:            	@param	errorState	errorstate to account for any errors during retrieval /creation of the secretKey
0710:            	@return the original unencrypted key bytes to use for encryption and decrytion   
0711:            	
0712:             */
0713:            private byte[] getDatabaseSecretKey(Properties properties,
0714:                    byte[] bootPassword, String errorState)
0715:                    throws StandardException {
0716:
0717:                // recover the generated secret encryption key from the
0718:                // services.properties file and the user key.
0719:                String keyString = properties
0720:                        .getProperty(RawStoreFactory.ENCRYPTED_KEY);
0721:                if (keyString == null)
0722:                    throw StandardException.newException(errorState);
0723:
0724:                int encodedKeyCharLength = keyString.indexOf('-');
0725:
0726:                if (encodedKeyCharLength == -1) // bad form
0727:                    throw StandardException.newException(errorState);
0728:
0729:                int verifyKey = Integer.parseInt(keyString
0730:                        .substring(encodedKeyCharLength + 1));
0731:                byte[] generatedKey = decryptKey(keyString,
0732:                        encodedKeyCharLength, bootPassword);
0733:
0734:                int checkKey = digest(generatedKey);
0735:
0736:                if (checkKey != verifyKey)
0737:                    throw StandardException.newException(errorState);
0738:
0739:                // if encodedKeyLength is not defined, then either it is an old version with no support for different
0740:                // key sizes and padding except for defaults
0741:                byte[] result;
0742:                if (encodedKeyLength != 0) {
0743:                    result = new byte[encodedKeyLength];
0744:
0745:                    // extract the generated key without the padding bytes
0746:                    System.arraycopy(generatedKey, 0, result, 0,
0747:                            encodedKeyLength);
0748:                    return result;
0749:                }
0750:
0751:                return generatedKey;
0752:            }
0753:
0754:            private String saveSecretKey(byte[] secretKey, byte[] bootPassword)
0755:                    throws StandardException {
0756:                String encryptedKey = encryptKey(secretKey, bootPassword);
0757:
0758:                // make a verification key out of the message digest of
0759:                // the generated key
0760:                int verifyKey = digest(secretKey);
0761:
0762:                return encryptedKey.concat("-" + verifyKey);
0763:
0764:            }
0765:
0766:            public String changeBootPassword(String changeString,
0767:                    Properties properties, CipherProvider verify)
0768:                    throws StandardException {
0769:
0770:                // the new bootPassword is expected to be of the form
0771:                // oldkey , newkey.
0772:                int seperator = changeString.indexOf(',');
0773:                if (seperator == -1)
0774:                    throw StandardException
0775:                            .newException(SQLState.WRONG_PASSWORD_CHANGE_FORMAT);
0776:
0777:                String oldBP = changeString.substring(0, seperator).trim();
0778:                byte[] oldBPAscii = StringUtil.getAsciiBytes(oldBP);
0779:                if (oldBPAscii == null
0780:                        || oldBPAscii.length < CipherFactory.MIN_BOOTPASS_LENGTH)
0781:                    throw StandardException
0782:                            .newException(SQLState.WRONG_BOOT_PASSWORD);
0783:                ;
0784:
0785:                String newBP = changeString.substring(seperator + 1).trim();
0786:                byte[] newBPAscii = StringUtil.getAsciiBytes(newBP);
0787:                if (newBPAscii == null
0788:                        || newBPAscii.length < CipherFactory.MIN_BOOTPASS_LENGTH)
0789:                    throw StandardException.newException(
0790:                            SQLState.ILLEGAL_BP_LENGTH, new Integer(
0791:                                    CipherFactory.MIN_BOOTPASS_LENGTH));
0792:
0793:                // verify old key
0794:
0795:                byte[] generatedKey = getDatabaseSecretKey(properties,
0796:                        oldBPAscii, SQLState.WRONG_BOOT_PASSWORD);
0797:
0798:                // make sure the oldKey is correct
0799:                byte[] IV = generateIV(generatedKey);
0800:
0801:                if (!((JCECipherProvider) verify).verifyIV(IV))
0802:                    throw StandardException
0803:                            .newException(SQLState.WRONG_BOOT_PASSWORD);
0804:
0805:                // Make the new key.  The generated key is unchanged, only the
0806:                // encrypted key is changed.
0807:                String newkey = saveSecretKey(generatedKey, newBPAscii);
0808:
0809:                properties.put(Attribute.CRYPTO_KEY_LENGTH, keyLengthBits + "-"
0810:                        + encodedKeyLength);
0811:
0812:                return saveSecretKey(generatedKey, newBPAscii);
0813:            }
0814:
0815:            /**
0816:             	perform actions with privileges enabled.
0817:             */
0818:            public final Object run() throws StandardException,
0819:                    InstantiationException, IllegalAccessException {
0820:
0821:                try {
0822:
0823:                    switch (action) {
0824:                    case 1:
0825:                        Security.addProvider((Provider) (Class
0826:                                .forName(cryptoProvider).newInstance()));
0827:                        break;
0828:                    case 2:
0829:                        // SECURITY PERMISSION - MP1 and/or OP4
0830:                        // depends on the value of activePerms
0831:                        return activeFile.getRandomAccessFile(activePerms);
0832:                    case 3:
0833:                        return activeFile.getInputStream();
0834:
0835:                    }
0836:
0837:                } catch (ClassNotFoundException cnfe) {
0838:                    throw StandardException.newException(
0839:                            SQLState.ENCRYPTION_NO_PROVIDER_CLASS,
0840:                            cryptoProvider);
0841:                } catch (FileNotFoundException fnfe) {
0842:                    throw StandardException.newException(
0843:                            SQLState.ENCRYPTION_UNABLE_KEY_VERIFICATION,
0844:                            cryptoProvider);
0845:                }
0846:                return null;
0847:            }
0848:
0849:            /**
0850:                The database can be encrypted with an encryption key given in connection url.
0851:                For security reasons, this key is not made persistent in the database.
0852:
0853:                But it is necessary to verify the encryption key when booting the database if it is similar
0854:                to the one used when creating the database
0855:                This needs to happen before we access the data/logs to avoid the risk of corrupting the 
0856:                database because of a wrong encryption key.
0857:
0858:                This method performs the steps necessary to verify the encryption key if an external
0859:                encryption key is given.
0860:
0861:                At database creation, 4k of random data is generated using SecureRandom and MD5 is used
0862:                to compute the checksum for the random data thus generated.  This 4k page of random data
0863:                is then encrypted using the encryption key. The checksum of unencrypted data and
0864:                encrypted data is made persistent in the database in file by name given by
0865:                Attribute.CRYPTO_EXTERNAL_KEY_VERIFYFILE (verifyKey.dat). This file exists directly under the
0866:                database root directory.
0867:
0868:                When trying to boot an existing encrypted database, the given encryption key is used to decrypt
0869:                the data in the verifyKey.dat and the checksum is calculated and compared against the original
0870:                stored checksum. If these checksums dont match an exception is thrown.
0871:
0872:                Please note, this process of verifying the key  does not provide any added security but only is 
0873:                intended to allow to fail gracefully if a wrong encryption key is used
0874:
0875:                StandardException is thrown if there are any problems during the process of verification
0876:                		of the encryption key or if there is any mismatch of the encryption key.
0877:
0878:             */
0879:
0880:            public void verifyKey(boolean create, StorageFactory sf,
0881:                    Properties properties) throws StandardException {
0882:
0883:                if (properties.getProperty(Attribute.CRYPTO_EXTERNAL_KEY) == null)
0884:                    return;
0885:
0886:                // if firstTime ( ie during creation of database, initial key used )
0887:                // In order to allow for verifying the external key for future database boot,
0888:                // generate random 4k of data and store the encrypted random data and the checksum
0889:                // using MD5 of the unencrypted data. That way, on next database boot a check is performed
0890:                // to verify if the key is the same as used when the database was created
0891:
0892:                InputStream verifyKeyInputStream = null;
0893:                StorageRandomAccessFile verifyKeyFile = null;
0894:                byte[] data = new byte[VERIFYKEY_DATALEN];
0895:                try {
0896:                    if (create) {
0897:                        getSecureRandom().nextBytes(data);
0898:                        // get the checksum
0899:                        byte[] checksum = getMD5Checksum(data);
0900:
0901:                        CipherProvider tmpCipherProvider = createNewCipher(
0902:                                ENCRYPT, mainSecretKey, mainIV);
0903:                        tmpCipherProvider
0904:                                .encrypt(data, 0, data.length, data, 0);
0905:                        // openFileForWrite
0906:                        verifyKeyFile = privAccessFile(sf,
0907:                                Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE, "rw");
0908:                        // write the checksum length as int, and then the checksum and then the encrypted data
0909:                        verifyKeyFile.writeInt(checksum.length);
0910:                        verifyKeyFile.write(checksum);
0911:                        verifyKeyFile.write(data);
0912:                        verifyKeyFile.sync(true);
0913:                    } else {
0914:                        // Read from verifyKey.dat as an InputStream. This allows for 
0915:                        // reading the information from verifyKey.dat successfully even when using the jar
0916:                        // subprotocol to boot derby. (DERBY-1373) 
0917:                        verifyKeyInputStream = privAccessGetInputStream(sf,
0918:                                Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
0919:                        DataInputStream dis = new DataInputStream(
0920:                                verifyKeyInputStream);
0921:                        // then read the checksum length 
0922:                        int checksumLen = dis.readInt();
0923:
0924:                        byte[] originalChecksum = new byte[checksumLen];
0925:                        dis.readFully(originalChecksum);
0926:
0927:                        dis.readFully(data);
0928:
0929:                        // decrypt data with key
0930:                        CipherProvider tmpCipherProvider = createNewCipher(
0931:                                DECRYPT, mainSecretKey, mainIV);
0932:                        tmpCipherProvider
0933:                                .decrypt(data, 0, data.length, data, 0);
0934:
0935:                        byte[] verifyChecksum = getMD5Checksum(data);
0936:
0937:                        if (!MessageDigest.isEqual(originalChecksum,
0938:                                verifyChecksum)) {
0939:                            throw StandardException
0940:                                    .newException(SQLState.ENCRYPTION_BAD_EXTERNAL_KEY);
0941:                        }
0942:
0943:                    }
0944:                } catch (IOException ioe) {
0945:                    throw StandardException.newException(
0946:                            SQLState.ENCRYPTION_UNABLE_KEY_VERIFICATION, ioe);
0947:                } finally {
0948:                    try {
0949:                        if (verifyKeyFile != null)
0950:                            verifyKeyFile.close();
0951:                        if (verifyKeyInputStream != null)
0952:                            verifyKeyInputStream.close();
0953:                    } catch (IOException ioee) {
0954:                        throw StandardException.newException(
0955:                                SQLState.ENCRYPTION_UNABLE_KEY_VERIFICATION,
0956:                                ioee);
0957:                    }
0958:                }
0959:                return;
0960:            }
0961:
0962:            /**
0963:            	Use MD5 MessageDigest algorithm to generate checksum
0964:            	@param data	data to be used to compute the hash value
0965:            	@return returns the hash value computed using the data
0966:
0967:             */
0968:            private byte[] getMD5Checksum(byte[] data) throws StandardException {
0969:                try {
0970:                    // get the checksum
0971:                    MessageDigest md5 = MessageDigest.getInstance("MD5");
0972:                    return md5.digest(data);
0973:                } catch (NoSuchAlgorithmException nsae) {
0974:                    throw StandardException.newException(
0975:                            SQLState.ENCRYPTION_BAD_ALG_FORMAT, MESSAGE_DIGEST);
0976:                }
0977:
0978:            }
0979:
0980:            /**
0981:             	access a file for either read/write
0982:             	@param storageFactory	factory used for io access
0983:             	@param	fileName		name of the file to create and open for write
0984:            						The file will be created directly under the database root directory
0985:            	@param	filePerms		file permissions, if "rw" open file with read and write permissions
0986:            						    if "r" , open file with read permissions
0987:             	@return	StorageRandomAccessFile returns file with fileName for writing
0988:            	@exception IOException Any exception during accessing the file for read/write
0989:             */
0990:            private StorageRandomAccessFile privAccessFile(
0991:                    StorageFactory storageFactory, String fileName,
0992:                    String filePerms) throws java.io.IOException {
0993:                StorageFile verifyKeyFile = storageFactory.newStorageFile("",
0994:                        fileName);
0995:                activeFile = verifyKeyFile;
0996:                this .action = 2;
0997:                activePerms = filePerms;
0998:                try {
0999:                    return (StorageRandomAccessFile) java.security.AccessController
1000:                            .doPrivileged(this );
1001:                } catch (java.security.PrivilegedActionException pae) {
1002:                    throw (java.io.IOException) pae.getException();
1003:                }
1004:            }
1005:
1006:            /**
1007:             access a InputStream for a given file for reading.
1008:             @param storageFactory   factory used for io access
1009:             @param  fileName        name of the file to open as a stream for reading
1010:             @return InputStream returns the stream for the file with fileName for reading
1011:             @exception IOException Any exception during accessing the file for read
1012:             */
1013:            private InputStream privAccessGetInputStream(
1014:                    StorageFactory storageFactory, String fileName)
1015:                    throws StandardException {
1016:                StorageFile verifyKeyFile = storageFactory.newStorageFile("",
1017:                        fileName);
1018:                activeFile = verifyKeyFile;
1019:                this .action = 3;
1020:                try {
1021:                    return (InputStream) java.security.AccessController
1022:                            .doPrivileged(this );
1023:                } catch (java.security.PrivilegedActionException pae) {
1024:                    throw (StandardException) pae.getException();
1025:                }
1026:            }
1027:
1028:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.