Source Code Cross Referenced for EncryptionManager.java in  » Database-DBMS » db-derby-10.2 » org » apache » derby » client » am » 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.client.am 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:
003:           Derby - Class org.apache.derby.client.am.EncryptionManager
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.client.am;
023:
024:        import java.security.Provider;
025:        import java.security.Security;
026:        import org.apache.derby.shared.common.reference.SQLState;
027:        import org.apache.derby.shared.common.sanity.SanityManager;
028:
029:        // This class is get used when using encrypted password and/or userid mechanism.
030:        // The <b>EncryptionManager</b> classs uses Diffie_Hellman algorithm to get the publick key and
031:        // secret key, and then DES encryption is done using certain token (based on security
032:        // mechanism) and server side's public key. Basically, this class is called when using
033:        // security mechanism User ID and encrypted password (usrencpwd) and Encrypted user ID and password
034:        // (eusridpwd).
035:        // This class uses JCE provider to do Diffie_Hellman algorithm and DES encryption,
036:        // obtainPublicKey(), calculateEncryptionToken(int, byte[]) and encryptData(byte[], int, byte[], byte[])
037:        // The agreed public value for the Diffie-Hellman prime is 256 bits
038:        // and hence the encrytion will work only if the jce provider supports a 256 bits prime
039:        //
040:        // This class also have methods for the SECMEC_USRSSBPWD security mechanism.
041:
042:        public class EncryptionManager {
043:            transient Agent agent_; // for obtaining an exception log writer only
044:
045:            // PROTOCOL's Diffie-Hellman agreed public value: prime.
046:            private static final byte modulusBytes__[] = { (byte) 0xC6,
047:                    (byte) 0x21, (byte) 0x12, (byte) 0xD7, (byte) 0x3E,
048:                    (byte) 0xE6, (byte) 0x13, (byte) 0xF0, (byte) 0x94,
049:                    (byte) 0x7A, (byte) 0xB3, (byte) 0x1F, (byte) 0x0F,
050:                    (byte) 0x68, (byte) 0x46, (byte) 0xA1, (byte) 0xBF,
051:                    (byte) 0xF5, (byte) 0xB3, (byte) 0xA4, (byte) 0xCA,
052:                    (byte) 0x0D, (byte) 0x60, (byte) 0xBC, (byte) 0x1E,
053:                    (byte) 0x4C, (byte) 0x7A, (byte) 0x0D, (byte) 0x8C,
054:                    (byte) 0x16, (byte) 0xB3, (byte) 0xE3 };
055:
056:            //the prime value in BigInteger form. It has to be in BigInteger form because this
057:            //is the form used in JCE library.
058:            private static final java.math.BigInteger modulus__ = new java.math.BigInteger(
059:                    1, modulusBytes__);
060:
061:            //  PROTOCOL's Diffie-Hellman agreed public value: base.
062:            private static final byte baseBytes__[] = { (byte) 0x46,
063:                    (byte) 0x90, (byte) 0xFA, (byte) 0x1F, (byte) 0x7B,
064:                    (byte) 0x9E, (byte) 0x1D, (byte) 0x44, (byte) 0x42,
065:                    (byte) 0xC8, (byte) 0x6C, (byte) 0x91, (byte) 0x14,
066:                    (byte) 0x60, (byte) 0x3F, (byte) 0xDE, (byte) 0xCF,
067:                    (byte) 0x07, (byte) 0x1E, (byte) 0xDC, (byte) 0xEC,
068:                    (byte) 0x5F, (byte) 0x62, (byte) 0x6E, (byte) 0x21,
069:                    (byte) 0xE2, (byte) 0x56, (byte) 0xAE, (byte) 0xD9,
070:                    (byte) 0xEA, (byte) 0x34, (byte) 0xE4 };
071:
072:            // The base value in BigInteger form.
073:            private static final java.math.BigInteger base__ = new java.math.BigInteger(
074:                    1, baseBytes__);
075:
076:            //PROTOCOL's Diffie-Hellman agreed exponential length
077:            private static final int exponential_length__ = 255;
078:
079:            private javax.crypto.spec.DHParameterSpec paramSpec_;
080:            private java.security.KeyPairGenerator keyPairGenerator_;
081:            private java.security.KeyPair keyPair_;
082:            private javax.crypto.KeyAgreement keyAgreement_;
083:
084:            private byte[] token_; // init vector
085:            private byte[] secKey_; // security key
086:            private javax.crypto.SecretKeyFactory secretKeyFactory_ = null;
087:            private String providerName; // security provider name
088:            private Provider provider;
089:
090:            // Required for SECMEC_USRSSBPWD DRDA security mechanism
091:            // NOTE: In a next incarnation, these constants are being moved
092:            // to a dedicated/specialized SecMec_USRSSBPWD class implementing
093:            // a SecurityMechanism interface.
094:            private java.security.MessageDigest messageDigest = null;
095:            private java.security.SecureRandom secureRandom = null;
096:            private final static int SECMEC_USRSSBPWD_SEED_LEN = 8; // Seed length
097:            // PWSEQs's 8-byte value constant - See DRDA Vol 3
098:            private static final byte SECMEC_USRSSBPWD_PWDSEQS[] = {
099:                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
100:                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 };
101:            // Random Number Generator (PRNG) Algorithm
102:            private final static String SHA_1_PRNG_ALGORITHM = "SHA1PRNG";
103:            public final static String SHA_1_DIGEST_ALGORITHM = "SHA-1";
104:
105:            // EncryptionManager constructor. In this constructor,DHParameterSpec,
106:            // KeyPairGenerator, KeyPair, and KeyAgreement  are initialized.
107:            public EncryptionManager(Agent agent) throws SqlException {
108:                agent_ = agent;
109:                try {
110:                    // get a security provider that supports the diffie helman key agreement algorithm
111:                    Provider[] list = Security.getProviders("KeyAgreement.DH");
112:                    if (list == null) {
113:                        throw new java.security.NoSuchProviderException();
114:                    }
115:                    provider = list[0];
116:                    providerName = provider.getName();
117:                    paramSpec_ = new javax.crypto.spec.DHParameterSpec(
118:                            modulus__, base__, exponential_length__);
119:                    keyPairGenerator_ = java.security.KeyPairGenerator
120:                            .getInstance("DH", providerName);
121:                    keyPairGenerator_.initialize(paramSpec_);
122:                    keyPair_ = keyPairGenerator_.generateKeyPair();
123:                    keyAgreement_ = javax.crypto.KeyAgreement.getInstance("DH",
124:                            providerName);
125:                    keyAgreement_.init(keyPair_.getPrivate());
126:                } catch (java.security.GeneralSecurityException e) {
127:                    throw new SqlException(agent_.logWriter_,
128:                            new ClientMessageId(
129:                                    SQLState.SECURITY_EXCEPTION_ENCOUNTERED), e);
130:                }
131:            }
132:
133:            // Retrieve a particular instance of the Encryption manager for a given
134:            // (Messsage Digest) algorithm. This is currently required for the
135:            // SECMEC_USRSSBPWD (strong password substitute) security mechanism.
136:            // 
137:            // NOTE: This is temporary logic as the encryption manager is being
138:            // rewritten into a DRDASecurityManager and have some of the
139:            // client/engine common logic moved to the Derby 'shared' package.
140:            public EncryptionManager(Agent agent, String algorithm)
141:                    throws SqlException {
142:                agent_ = agent;
143:                try {
144:                    // Instantiate the encryption manager for the passed-in security
145:                    // algorithm and this from the default provider
146:                    // NOTE: We're only dealing with Message Digest algorithms for now.
147:                    messageDigest = java.security.MessageDigest
148:                            .getInstance(algorithm);
149:                    // We're also verifying that we can instantiate a randon number
150:                    // generator (PRNG).
151:                    secureRandom = java.security.SecureRandom
152:                            .getInstance(SHA_1_PRNG_ALGORITHM);
153:                } catch (java.security.NoSuchAlgorithmException nsae) {
154:                    // The following exception should not be raised for SHA-1 type of
155:                    // message digest as we've already verified during boot-up that this
156:                    // algorithm was available as part of the JRE (since BUILT-IN
157:                    // authentication requires it); but we still raise the exception if
158:                    // a client were to request a different algorithm.
159:                    throw new SqlException(agent_.logWriter_,
160:                            new ClientMessageId(
161:                                    SQLState.SECURITY_EXCEPTION_ENCOUNTERED),
162:                            nsae);
163:                }
164:            }
165:
166:            // This method generates the public key and returns it. This
167:            // shared public key is the application requester's connection key and will
168:            // be exchanged with the application server's connection key. This connection
169:            // key will be put in the sectkn in ACCSEC command and send to the application
170:            // server.
171:            // @param   null
172:            // @return  a byte array that is the application requester's public key
173:            public byte[] obtainPublicKey() {
174:
175:                //we need to get the plain form public key because PROTOCOL accepts plain form
176:                //public key only.
177:                java.math.BigInteger aPub = ((javax.crypto.interfaces.DHPublicKey) keyPair_
178:                        .getPublic()).getY();
179:                byte[] aPubKey = aPub.toByteArray();
180:
181:                //the following lines of code is to adjust the length of the key. PublicKey
182:                //in JCE is in the form of BigInteger and it's a signed value. When tranformed
183:                //to a Byte array form, normally this array is 32 bytes. However, if the
184:                //value happens to take up all 32 X 8 bits and it is positive, an extra
185:                //bit is needed and then a 33 byte array will be returned. Since PROTOCOL can't
186:                //recogize the 33 byte key, we check the length here, if the length is 33,
187:                //we will just trim off the first byte (0) and get the rest of 32 bytes.
188:                if (aPubKey.length == 33 && aPubKey[0] == 0) {
189:                    //System.out.println ("Adjust length");
190:                    byte[] newKey = new byte[32];
191:                    for (int i = 0; i < newKey.length; i++) {
192:                        newKey[i] = aPubKey[i + 1];
193:                    }
194:                    return newKey;
195:                }
196:
197:                //the following lines of code is to adjust the length of the key. Occasionally,
198:                //the length of the public key is less than 32, the reason of this is that the 0 byte
199:                //in the beginning is somehow not returned. So we check the length here, if the length
200:                //is less than 32, we will pad 0 in the beginning to make the public key 32 bytes
201:                if (aPubKey.length < 32) {
202:                    byte[] newKey = new byte[32];
203:                    int i;
204:                    for (i = 0; i < 32 - aPubKey.length; i++) {
205:                        newKey[i] = 0;
206:                    }
207:                    for (int j = i; j < newKey.length; j++) {
208:                        newKey[j] = aPubKey[j - i];
209:                    }
210:                    return newKey;
211:                }
212:                return aPubKey;
213:            }
214:
215:            // This method is used to calculate the encryption token. DES encrypts the
216:            // data using a token and the generated shared private key. The token used
217:            // depends on the type of security mechanism being used:
218:            // USRENCPWD - The userid is used as the token. The USRID is zero-padded to
219:            // 8 bytes if less than 8 bytes or truncated to 8 bytes if greater than 8 bytes.
220:            // EUSRIDPWD - The middle 8 bytes of the server's connection key is used as
221:            // the token.
222:            // @param  int     securityMechanism
223:            // @param  byte[]  userid or server's connection key
224:            // @return byte[]  the encryption token
225:            private byte[] calculateEncryptionToken(int securityMechanism,
226:                    byte[] initVector) {
227:                byte[] token = new byte[8];
228:
229:                //USRENCPWD, the userid is used as token
230:                if (securityMechanism == 7) {
231:                    if (initVector.length < 8) { //shorter than 8 bytes, zero padded to 8 bytes
232:                        for (int i = 0; i < initVector.length; i++) {
233:                            token[i] = initVector[i];
234:                        }
235:                        for (int i = initVector.length; i < 8; i++) {
236:                            token[i] = 0;
237:                        }
238:                    } else { //longer than 8 bytes, truncated to 8 bytes
239:                        for (int i = 0; i < 8; i++) {
240:                            token[i] = initVector[i];
241:                        }
242:                    }
243:                }
244:                //EUSRIDPWD - The middle 8 bytes of the server's connection key is used as
245:                //the token.
246:                else if (securityMechanism == 9) {
247:                    for (int i = 0; i < 8; i++) {
248:                        token[i] = initVector[i + 12];
249:                    }
250:                }
251:                return token;
252:            }
253:
254:            //JDK 1.4 has a parity check on the DES encryption key. Each byte needs to have an odd number
255:            //of "1"s in it, and this is required by DES. Otherwise JDK 1.4 throws InvalidKeyException.
256:            //Older JDK doesn't check this. In order to make encryption work with JDK1.4, we are going to
257:            //check each of the 8 byte of our key and flip the last bit if it has even number of 1s.
258:            private void keyParityCheck(byte[] key) throws SqlException {
259:                byte temp;
260:                int changeParity;
261:                if (key.length != 8) {
262:                    throw new SqlException(agent_.logWriter_,
263:                            new ClientMessageId(
264:                                    SQLState.DES_KEY_HAS_WRONG_LENGTH),
265:                            new Integer(8), new Integer(key.length));
266:
267:                }
268:                for (int i = 0; i < 8; i++) {
269:                    temp = key[i];
270:                    changeParity = 1;
271:                    for (int j = 0; j < 8; j++) {
272:                        if (temp < 0) {
273:                            changeParity = 1 - changeParity;
274:                        }
275:                        temp = (byte) (temp << 1);
276:                    }
277:                    if (changeParity == 1) {
278:                        if ((key[i] & 1) != 0) {
279:                            key[i] &= 0xfe;
280:                        } else {
281:                            key[i] |= 1;
282:                        }
283:                    }
284:                }
285:            }
286:
287:            // This method generates a secret key using the application server's
288:            // public key
289:            private byte[] generatePrivateKey(byte[] targetPublicKey)
290:                    throws SqlException {
291:                try {
292:
293:                    //initiate a Diffie_Hellman KeyFactory object.
294:                    java.security.KeyFactory keyFac = java.security.KeyFactory
295:                            .getInstance("DH", provider);
296:
297:                    //Use server's public key to initiate a DHPublicKeySpec and then use
298:                    //this DHPublicKeySpec to initiate a publicKey object
299:                    java.math.BigInteger publicKey = new java.math.BigInteger(
300:                            1, targetPublicKey);
301:                    javax.crypto.spec.DHPublicKeySpec dhKeySpec = new javax.crypto.spec.DHPublicKeySpec(
302:                            publicKey, modulus__, base__);
303:                    java.security.PublicKey pubKey = keyFac
304:                            .generatePublic(dhKeySpec);
305:
306:                    //Execute the first phase of DH keyagreement protocal.
307:                    keyAgreement_.doPhase(pubKey, true);
308:
309:                    //generate the shared secret key. The application requestor's shared secret
310:                    //key should be exactly the same as the application server's shared secret
311:                    //key
312:                    byte[] sharedSecret = keyAgreement_.generateSecret();
313:                    byte[] newKey = new byte[32];
314:
315:                    //We adjust the length here. If the length of secret key is 33 and the first byte is 0,
316:                    //we trim off the frist byte. If the length of secret key is less than 32, we will
317:                    //pad 0 to the beginning of the byte array tho make the secret key 32 bytes.
318:                    if (sharedSecret.length == 33 && sharedSecret[0] == 0) {
319:                        for (int i = 0; i < newKey.length; i++) {
320:                            newKey[i] = sharedSecret[i + 1];
321:                        }
322:
323:                    }
324:                    if (sharedSecret.length < 32) {
325:                        int i;
326:                        for (i = 0; i < (32 - sharedSecret.length); i++) {
327:                            newKey[i] = 0;
328:                        }
329:                        for (int j = i; j < sharedSecret.length; j++) {
330:                            newKey[j] = sharedSecret[j - i];
331:                        }
332:                    }
333:
334:                    //The Data Encryption Standard (DES) is going to be used to encrypt userid
335:                    //and password. DES is a block cipher; it encrypts data in 64-bit blocks.
336:                    //PROTOCOL encryption uses DES CBC mode as defined by the FIPS standard
337:                    //DES CBC requires an encryption key and an 8 byte token to encrypt the data.
338:                    //The middle 8 bytes of Diffie-Hellman shared private key is used as the
339:                    //encryption key. The following code retrieves middle 8 bytes of the shared
340:                    //private key.
341:                    byte[] key = new byte[8];
342:
343:                    //if secret key is not 32, we will use the adjust length secret key
344:                    if (sharedSecret.length == 32) {
345:                        for (int i = 0; i < 8; i++) {
346:                            key[i] = sharedSecret[i + 12];
347:                        }
348:                    } else if (sharedSecret.length == 33
349:                            || sharedSecret.length < 32) {
350:                        for (int i = 0; i < 8; i++) {
351:                            key[i] = newKey[i + 12];
352:                        }
353:                    } else {
354:                        throw new SqlException(agent_.logWriter_,
355:                                new ClientMessageId(
356:                                        SQLState.SHARED_KEY_LENGTH_ERROR),
357:                                new Integer(sharedSecret.length));
358:                    }
359:
360:                    //we do parity check here and flip the parity bit if the byte has even number of 1s
361:                    keyParityCheck(key);
362:                    return key;
363:                } catch (java.security.GeneralSecurityException e) {
364:                    throw new SqlException(agent_.logWriter_,
365:                            new ClientMessageId(
366:                                    SQLState.SECURITY_EXCEPTION_ENCOUNTERED), e);
367:                }
368:            }
369:
370:            // This method encrypts the usreid/password with the middle 8 bytes of
371:            // the generated secret key and an encryption token. Then it returns the
372:            // encrypted data in a byte array.
373:            // plainText   The byte array form userid/password to encrypt.
374:            // initVector  The byte array which is used to calculate the
375:            //                             encryption token.
376:            // targetPublicKey   DERBY' public key.
377:            // Returns the encrypted data in a byte array.
378:            public byte[] encryptData(byte[] plainText, int securityMechanism,
379:                    byte[] initVector, byte[] targetPublicKey)
380:                    throws SqlException {
381:
382:                byte[] cipherText = null;
383:                java.security.Key key = null;
384:
385:                if (token_ == null) {
386:                    token_ = calculateEncryptionToken(securityMechanism,
387:                            initVector);
388:                }
389:
390:                try {
391:                    if (secKey_ == null) {
392:                        //use this encryption key to initiate a SecretKeySpec object
393:                        secKey_ = generatePrivateKey(targetPublicKey);
394:                        javax.crypto.spec.SecretKeySpec desKey = new javax.crypto.spec.SecretKeySpec(
395:                                secKey_, "DES");
396:                        key = desKey;
397:                    } else {
398:                        //use this encryption key to initiate a SecretKeySpec object
399:                        javax.crypto.spec.DESKeySpec desKey = new javax.crypto.spec.DESKeySpec(
400:                                secKey_);
401:                        if (secretKeyFactory_ == null) {
402:                            secretKeyFactory_ = javax.crypto.SecretKeyFactory
403:                                    .getInstance("DES", providerName);
404:                        }
405:                        key = secretKeyFactory_.generateSecret(desKey);
406:                    }
407:
408:                    //We use DES in CBC mode because this is the mode used in PROTOCOL. The
409:                    //encryption mode has to be consistent for encryption and decryption.
410:                    //CBC mode requires an initialization vector(IV) parameter. In CBC mode
411:                    //we need to initialize the Cipher object with an IV, which can be supplied
412:                    // using the javax.crypto.spec.IvParameterSpec class.
413:                    javax.crypto.Cipher cipher = javax.crypto.Cipher
414:                            .getInstance("DES/CBC/PKCS5Padding", providerName);
415:
416:                    //generate a IVParameterSpec object and use it to initiate the
417:                    //Cipher object.
418:                    javax.crypto.spec.IvParameterSpec ivParam = new javax.crypto.spec.IvParameterSpec(
419:                            token_);
420:
421:                    //initiate the Cipher using encryption mode, encryption key and the
422:                    //IV parameter.
423:                    cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, ivParam);
424:
425:                    //Execute the final phase of encryption
426:                    cipherText = cipher.doFinal(plainText);
427:                } catch (javax.crypto.NoSuchPaddingException e) {
428:                    throw new SqlException(
429:                            agent_.logWriter_,
430:                            new ClientMessageId(SQLState.CRYPTO_NO_SUCH_PADDING));
431:                } catch (javax.crypto.BadPaddingException e) {
432:                    throw new SqlException(agent_.logWriter_,
433:                            new ClientMessageId(SQLState.CRYPTO_BAD_PADDING));
434:                } catch (javax.crypto.IllegalBlockSizeException e) {
435:                    throw new SqlException(agent_.logWriter_,
436:                            new ClientMessageId(
437:                                    SQLState.CRYPTO_ILLEGAL_BLOCK_SIZE));
438:                } catch (java.security.GeneralSecurityException e) {
439:                    throw new SqlException(agent_.logWriter_,
440:                            new ClientMessageId(
441:                                    SQLState.SECURITY_EXCEPTION_ENCOUNTERED), e);
442:                }
443:
444:                return cipherText;
445:            }
446:
447:            // This method decrypts the usreid/password with the middle 8 bytes of
448:            // the generated secret key and an encryption token. Then it returns the
449:            // decrypted data in a byte array.
450:            // plainText   The byte array form userid/password to encrypt.
451:            // initVector  The byte array which is used to calculate the
452:            //                             encryption token.
453:            // targetPublicKey   DERBY' public key.
454:            // Returns the decrypted data in a byte array.
455:            public byte[] decryptData(byte[] cipherText, int securityMechanism,
456:                    byte[] initVector, byte[] targetPublicKey)
457:                    throws SqlException {
458:
459:                byte[] plainText = null;
460:                java.security.Key key = null;
461:
462:                if (token_ == null) {
463:                    token_ = calculateEncryptionToken(securityMechanism,
464:                            initVector);
465:                }
466:
467:                try {
468:                    if (secKey_ == null) {
469:                        //use this encryption key to initiate a SecretKeySpec object
470:                        secKey_ = generatePrivateKey(targetPublicKey);
471:                        javax.crypto.spec.SecretKeySpec desKey = new javax.crypto.spec.SecretKeySpec(
472:                                secKey_, "DES");
473:                        key = desKey;
474:                    } else {
475:                        //use this encryption key to initiate a SecretKeySpec object
476:                        javax.crypto.spec.DESKeySpec desKey = new javax.crypto.spec.DESKeySpec(
477:                                secKey_);
478:                        if (secretKeyFactory_ == null) {
479:                            secretKeyFactory_ = javax.crypto.SecretKeyFactory
480:                                    .getInstance("DES", providerName);
481:                        }
482:                        key = secretKeyFactory_.generateSecret(desKey);
483:                    }
484:
485:                    //We use DES in CBC mode because this is the mode used in PROTOCOL. The
486:                    //encryption mode has to be consistent for encryption and decryption.
487:                    //CBC mode requires an initialization vector(IV) parameter. In CBC mode
488:                    //we need to initialize the Cipher object with an IV, which can be supplied
489:                    // using the javax.crypto.spec.IvParameterSpec class.
490:                    javax.crypto.Cipher cipher = javax.crypto.Cipher
491:                            .getInstance("DES/CBC/PKCS5Padding", providerName);
492:
493:                    //generate a IVParameterSpec object and use it to initiate the
494:                    //Cipher object.
495:                    javax.crypto.spec.IvParameterSpec ivParam = new javax.crypto.spec.IvParameterSpec(
496:                            token_);
497:
498:                    //initiate the Cipher using encryption mode, encryption key and the
499:                    //IV parameter.
500:                    cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key, ivParam);
501:
502:                    //Execute the final phase of encryption
503:                    plainText = cipher.doFinal(cipherText);
504:                } catch (javax.crypto.NoSuchPaddingException e) {
505:                    throw new SqlException(
506:                            agent_.logWriter_,
507:                            new ClientMessageId(SQLState.CRYPTO_NO_SUCH_PADDING));
508:                } catch (javax.crypto.BadPaddingException e) {
509:                    throw new SqlException(agent_.logWriter_,
510:                            new ClientMessageId(SQLState.CRYPTO_BAD_PADDING));
511:                } catch (javax.crypto.IllegalBlockSizeException e) {
512:                    throw new SqlException(agent_.logWriter_,
513:                            new ClientMessageId(
514:                                    SQLState.CRYPTO_ILLEGAL_BLOCK_SIZE));
515:                } catch (java.security.GeneralSecurityException e) {
516:                    throw new SqlException(agent_.logWriter_,
517:                            new ClientMessageId(
518:                                    SQLState.SECURITY_EXCEPTION_ENCOUNTERED), e);
519:                }
520:                return plainText;
521:            }
522:
523:            public void setInitVector(byte[] initVector) {
524:                token_ = initVector;
525:            }
526:
527:            public void setSecKey(byte[] secKey) {
528:                secKey_ = secKey;
529:            }
530:
531:            public void resetSecurityKeys() {
532:                token_ = null;
533:                secKey_ = null;
534:            }
535:
536:            /****************************************************************
537:             * Below are methods for the SECMEC_USRSSBPWD security mechanism.
538:             ****************************************************************/
539:
540:            /**
541:             * This method generates an 8-Byte random seed for the client (source).
542:             *
543:             * @return a random 8-Byte seed.
544:             */
545:            public byte[] generateSeed() {
546:                byte randomSeedBytes[] = new byte[SECMEC_USRSSBPWD_SEED_LEN];
547:                secureRandom.setSeed(secureRandom
548:                        .generateSeed(SECMEC_USRSSBPWD_SEED_LEN));
549:                secureRandom.nextBytes(randomSeedBytes);
550:                return randomSeedBytes;
551:            }
552:
553:            /**
554:             * Strong Password Substitution (USRSSBPWD).
555:             *
556:             * This method generate a password subtitute to send to the target
557:             * server.
558:             * 
559:             * Substitution algorithm works as follow:
560:             *
561:             * PW_TOKEN = SHA-1(PW, ID)
562:             * The password (PW) and user name (ID) can be of any length greater
563:             * than or equal to 1 byte.
564:             * The client generates a 20-byte password substitute (PW_SUB) as follows:
565:             * PW_SUB = SHA-1(PW_TOKEN, RDr, RDs, ID, PWSEQs)
566:             * 
567:             * w/ (RDs) as the random client seed and (RDr) as the server one.
568:             * 
569:             * See PWDSSB - Strong Password Substitution Security Mechanism
570:             * (DRDA Vol.3 - P.650)
571:             *
572:             * @param userName The user's name
573:             * @param password The user's password
574:             * @param sourceSeed_ random client seed (RDs)
575:             * @param targetSeed_ random server seed (RDr)
576:             *
577:             * @return a password substitute.
578:             */
579:            public byte[] substitutePassword(String userName, String password,
580:                    byte[] sourceSeed_, byte[] targetSeed_) throws SqlException {
581:
582:                // Pattern that is prefixed to the BUILTIN encrypted password
583:                String ID_PATTERN_NEW_SCHEME = "3b60";
584:
585:                // Generated password substitute
586:                byte[] passwordSubstitute;
587:
588:                // Assert we have a SHA-1 Message Digest already instantiated
589:                if (SanityManager.DEBUG) {
590:                    SanityManager.ASSERT((messageDigest != null)
591:                            && (SHA_1_DIGEST_ALGORITHM.equals(messageDigest
592:                                    .getAlgorithm())));
593:                }
594:
595:                // IMPORTANT NOTE: As the password is stored single-hashed in the
596:                // database on the target side, it is impossible for the target to
597:                // decrypt the password and recompute a substitute to compare with
598:                // one generated on the source side - Hence, for now we have to
599:                // single-hash and encrypt the password the same way the target is
600:                // doing it and we will still generate a substitute obviously - The
601:                // password, even pre-hashed will never make it across the wire as
602:                // a substitute is generated. In other words, if the target cannot
603:                // figure what the original password is (because of not being able
604:                // to decrypt it or not being able to retrieve it (i.e. LDAP), then
605:                // It may be problematic - so in a way, Strong Password Substitution
606:                // (USRSSBPWD) cannot be supported for targets which can't access or
607:                // decrypt some password on their side.
608:                //
609:                // So in short, SECMEC_USRSSBPWD is only supported if the
610:                // authentication provider on the target side is NONE or Derby's
611:                // BUILTIN one and if using Derby's Client Network driver (for now).
612:                //
613:                // Encrypt the password as it is done by the derby engine - Note that
614:                // this code (logic) is not shared yet - will be in next revision.
615:                messageDigest.reset();
616:
617:                messageDigest.update(this .toHexByte(password, 0, password
618:                        .length()));
619:                byte[] encryptVal = messageDigest.digest();
620:                String hexString = ID_PATTERN_NEW_SCHEME
621:                        + this .toHexString(encryptVal, 0, encryptVal.length);
622:
623:                // Generate some 20-byte password token
624:                byte[] userBytes = this .toHexByte(userName, 0, userName
625:                        .length());
626:                messageDigest.update(userBytes);
627:                messageDigest.update(this .toHexByte(hexString, 0, hexString
628:                        .length()));
629:                byte[] passwordToken = messageDigest.digest();
630:
631:                // Now we generate the 20-byte password substitute
632:                messageDigest.update(passwordToken);
633:                messageDigest.update(targetSeed_);
634:                messageDigest.update(sourceSeed_);
635:                messageDigest.update(userBytes);
636:                messageDigest.update(SECMEC_USRSSBPWD_PWDSEQS);
637:
638:                passwordSubstitute = messageDigest.digest();
639:
640:                return passwordSubstitute;
641:            }
642:
643:            /*********************************************************************
644:             * RESOLVE:                                                          *
645:             * The methods and static vars below should go into some 'shared'    *
646:             * package when the capability is put back in (StringUtil.java).     *
647:             *********************************************************************/
648:
649:            private static char[] hex_table = { '0', '1', '2', '3', '4', '5',
650:                    '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
651:
652:            /**
653:                Convert a byte array to a String with a hexidecimal format.
654:                The String may be converted back to a byte array using fromHexString.
655:                <BR>
656:                For each byte (b) two characaters are generated, the first character
657:                represents the high nibble (4 bits) in hexidecimal (<code>b & 0xf0</code>),
658:                the second character represents the low nibble (<code>b & 0x0f</code>).
659:                <BR>
660:                The byte at <code>data[offset]</code> is represented by the first two
661:                characters in the returned String.
662:
663:                @param	data	byte array
664:                @param	offset	starting byte (zero based) to convert.
665:                @param	length	number of bytes to convert.
666:
667:                @return the String (with hexidecimal format) form of the byte array
668:             */
669:            private String toHexString(byte[] data, int offset, int length) {
670:                StringBuffer s = new StringBuffer(length * 2);
671:                int end = offset + length;
672:
673:                for (int i = offset; i < end; i++) {
674:                    int high_nibble = (data[i] & 0xf0) >>> 4;
675:                    int low_nibble = (data[i] & 0x0f);
676:                    s.append(hex_table[high_nibble]);
677:                    s.append(hex_table[low_nibble]);
678:                }
679:
680:                return s.toString();
681:            }
682:
683:            /**
684:            
685:                Convert a string into a byte array in hex format.
686:                <BR>
687:                For each character (b) two bytes are generated, the first byte 
688:                represents the high nibble (4 bits) in hexidecimal (<code>b & 0xf0</code>),
689:                the second byte represents the low nibble (<code>b & 0x0f</code>).
690:                <BR>
691:                The character at <code>str.charAt(0)</code> is represented by the first two bytes 
692:                in the returned String.
693:
694:                @param	str string 
695:                @param	offset	starting character (zero based) to convert.
696:                @param	length	number of characters to convert.
697:
698:                @return the byte[]  (with hexidecimal format) form of the string (str) 
699:             */
700:            private byte[] toHexByte(String str, int offset, int length) {
701:                byte[] data = new byte[(length - offset) * 2];
702:                int end = offset + length;
703:
704:                for (int i = offset; i < end; i++) {
705:                    char ch = str.charAt(i);
706:                    int high_nibble = (ch & 0xf0) >>> 4;
707:                    int low_nibble = (ch & 0x0f);
708:                    data[i] = (byte) high_nibble;
709:                    data[i + 1] = (byte) low_nibble;
710:                }
711:                return data;
712:            }
713:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.