Source Code Cross Referenced for Record.java in  » 6.0-JDK-Modules » j2me » com » sun » midp » ssl » 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 » 6.0 JDK Modules » j2me » com.sun.midp.ssl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *   
0003:         *
0004:         * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006:         * 
0007:         * This program is free software; you can redistribute it and/or
0008:         * modify it under the terms of the GNU General Public License version
0009:         * 2 only, as published by the Free Software Foundation.
0010:         * 
0011:         * This program is distributed in the hope that it will be useful, but
0012:         * WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014:         * General Public License version 2 for more details (a copy is
0015:         * included at /legal/license.txt).
0016:         * 
0017:         * You should have received a copy of the GNU General Public License
0018:         * version 2 along with this work; if not, write to the Free Software
0019:         * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020:         * 02110-1301 USA
0021:         * 
0022:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023:         * Clara, CA 95054 or visit www.sun.com if you need additional
0024:         * information or have any questions.
0025:         */
0026:
0027:        package com.sun.midp.ssl;
0028:
0029:        import java.io.InputStream;
0030:        import java.io.OutputStream;
0031:        import java.io.IOException;
0032:        import java.io.InterruptedIOException;
0033:
0034:        import com.sun.midp.log.Logging;
0035:        import com.sun.midp.log.LogChannels;
0036:
0037:        import com.sun.midp.crypto.*;
0038:        import com.sun.midp.pki.*;
0039:
0040:        /**
0041:         * Implements an SSL record layer that sits atop a TCP connection
0042:         * and beneath the user-visible interface to an SSL socket. It 
0043:         * maintains all the state information necessary to encode/decode
0044:         * application data.
0045:         */
0046:        class Record {
0047:            /*
0048:             * IMPL_NOTE: We should try to avoid multiple buffer copies by defining a 
0049:             * new message class that allocates a large enough buffer to begin with 
0050:             * so new headers can be prepended and MAC/padding can be appended by 
0051:             * simple pointer manipulation.
0052:             */
0053:
0054:            /*
0055:             * SSLRecord types: CCS (Change Cipher Spec), ALRT (Alert),
0056:             * HNDSHK (Handshake) and APP (Application Data)
0057:             */
0058:            /** Change Cipher Spec (20). */
0059:            static final byte CCS = 20;
0060:            /** Alert (21). */
0061:            static final byte ALRT = 21;
0062:            /** Handshake (22). */
0063:            static final byte HNDSHK = 22;
0064:            /** Application data (23). */
0065:            static final byte APP = 23;
0066:
0067:            // There are two severity levels for alerts
0068:            /** Warning severity level for alerts (1). */
0069:            static final byte WARNING = 1;
0070:            /** Fatal severity level for alerts (2). */
0071:            static final byte FATAL = 2;
0072:
0073:            // Next, we have various Alert types
0074:            /** Close notification alert type (0). */
0075:            static final byte CLOSE_NTFY = 0;
0076:            /** Unexpected message alert type (10). */
0077:            static final byte UNEXP_MSG = 10;
0078:            /** Bad MAC alert type (20). */
0079:            static final byte BAD_MAC = 20;
0080:            // static final byte DECOMP_FAIL = 30;  we do not support compression
0081:            /** Handshake failure alert type (40). */
0082:            static final byte HNDSHK_FAIL = 40;
0083:            /** No certificate found alert type (41). */
0084:            static final byte NO_CERT = 41;
0085:            /** Bad certificate alert type (42). */
0086:            static final byte BAD_CERT = 42;
0087:            /** Unsupported certificate alert type (43). */
0088:            static final byte UNSUP_CERT = 43;
0089:            /** Certificate revoked alert type (44). */
0090:            static final byte CERT_REVKD = 44;
0091:            /** Certificate expired alaert type (45). */
0092:            static final byte CERT_EXPRD = 45;
0093:            /** Unknown certificate feature alert type (46). */
0094:            static final byte CERT_UNKWN = 46;
0095:            /** Bad parameter alert type (47). */
0096:            static final byte BAD_PARAM = 47;
0097:
0098:            /*
0099:             * Possible roles for this SSL record layer (client or server).
0100:             */
0101:            /** Server role for SSL record layout (0). */
0102:            static final byte SERVER = 0;
0103:            /** Client role for SSL record layout (1). */
0104:            static final byte CLIENT = 1;
0105:
0106:            /** Size of record header */
0107:            private final int HEADER_SIZE = 5;
0108:            /** Underlying input stream beneath the record layer. */
0109:            private InputStream in;
0110:            /** Underlying output stream beneath the record layer. */
0111:            private OutputStream out;
0112:
0113:            // Connection state information
0114:            /** Flag indicating change cipher spec received. */
0115:            private byte rActive = 0;
0116:            /** Flag indicating change cipher spec has been sent. */
0117:            private byte wActive = 0;
0118:
0119:            /** The SSL version in one byte (0x30=3.0). */
0120:            private byte ver;
0121:            /** Current input record header. */
0122:            private byte[] inputHeader = new byte[HEADER_SIZE];
0123:            /** How many bytes of the record header have been read. */
0124:            private int headerBytesRead;
0125:            /** How many bytes of the data in the record. */
0126:            private int dataLength;
0127:            /** How many bytes of the data in the record have been read. */
0128:            private int dataBytesRead;
0129:            /** Shutdown flag, true if connection has been shutdown. */
0130:            private boolean shutdown;
0131:
0132:            /** Current input record data. */
0133:            byte[] inputData;
0134:            /** Length of the plain text in the input buffer */
0135:            int plainTextLength;
0136:
0137:            /** Records encoder */
0138:            private RecordEncoder encoder = null;
0139:            /** Records decoder */
0140:            private RecordDecoder decoder = null;
0141:
0142:            /**
0143:             * Creates a new SSL record layer.
0144:             * 
0145:             * @param ins  input stream belonging to the underlying TCP connection
0146:             * @param outs output stream belonging to the underlying TCP connection
0147:             */
0148:            Record(InputStream ins, OutputStream outs) {
0149:                in = ins;
0150:                out = outs;
0151:                ver = (byte) 0x30; // IMPL_NOTE: This is hardcoded for now
0152:            }
0153:
0154:            /**
0155:             * Chops up a master secret into the client and server MAC secrets, 
0156:             * bulk encryption keys and IVs. Also initializes the Cipher and 
0157:             * MessageDigest objects used in record encoding/decoding. 
0158:             * 
0159:             * @param role role (either CLIENT or SERVER) of this side in the SSL 
0160:             *             negotiation
0161:             * @param clientRand 32-byte random value chosen by the client
0162:             * @param serverRand 32-byte random value chosen by the server
0163:             * @param suite negotiated cipher suite
0164:             * @param masterSecret master secret resulting from the key exchange
0165:             *
0166:             * @exception Exception if the negotiated cipher suite involves an 
0167:             *                      unsupported hash or cipher algorithm
0168:             */
0169:            void init(byte role, byte[] clientRand, byte[] serverRand,
0170:                    byte suite, byte[] masterSecret) throws Exception {
0171:                CipherSuiteData data = new CipherSuiteData(suite);
0172:                data.generateKeys(clientRand, serverRand, masterSecret);
0173:
0174:                // depending on role, we choose corresponding MAC secrets 
0175:                // and cipher bulk keys for encoder and decoder
0176:                byte[] encodeSecret;
0177:                byte[] decodeSecret;
0178:                SecretKey decodeCipherKey;
0179:                SecretKey encodeCipherKey;
0180:                if (role == CLIENT) {
0181:                    encodeSecret = data.getClientMACSecret();
0182:                    decodeSecret = data.getServerMACSecret();
0183:                    encodeCipherKey = data.getClientBulkKey();
0184:                    decodeCipherKey = data.getServerBulkKey();
0185:                } else {
0186:                    encodeSecret = data.getServerMACSecret();
0187:                    decodeSecret = data.getClientMACSecret();
0188:                    encodeCipherKey = data.getServerBulkKey();
0189:                    decodeCipherKey = data.getClientBulkKey();
0190:                }
0191:
0192:                Cipher encodeCipher = data.getEncodeCipher();
0193:                encodeCipher.init(Cipher.ENCRYPT_MODE, encodeCipherKey);
0194:                Cipher decodeCipher = data.getDecodeCipher();
0195:                decodeCipher.init(Cipher.DECRYPT_MODE, decodeCipherKey);
0196:
0197:                encoder = new RecordEncoder(data.getEncodeDigest(),
0198:                        encodeSecret, data.getPadLength(), encodeCipher);
0199:                decoder = new RecordDecoder(data.getDecodeDigest(),
0200:                        decodeSecret, data.getPadLength(), decodeCipher);
0201:            }
0202:
0203:            /**
0204:             * Reads and returns a record (including the 5-byte header) of 
0205:             * the specified type. If the caller asks for application data 
0206:             * and a close_notify warning alert is found as the next available
0207:             * record, this method sets plainTextLength to -1 to signal the end of the 
0208:             * input stream.
0209:             *
0210:             * @param block if true the method will not return until data is available,
0211:             *              or end of stream
0212:             * @param type desired SSL record type
0213:             *
0214:             * @exception IOException if an unexpected record type or SSL alert is
0215:             *                        found in the underlying sockets input stream 
0216:             */
0217:            void rdRec(boolean block, byte type) throws IOException {
0218:                if (!rdRec(block)) {
0219:                    return;
0220:                }
0221:
0222:                if (inputHeader[0] == type) {
0223:                    // success
0224:                    return;
0225:                }
0226:
0227:                // Signal end of stream.
0228:                plainTextLength = -1;
0229:
0230:                switch (inputHeader[0]) {
0231:                case CCS:
0232:                    // Can change_cipher_spec can be passed to handshake clients?
0233:                    // if (type == HNDSHK) return r; // fall through otherwise
0234:                case HNDSHK:
0235:                case APP:
0236:                default:
0237:                    alert(FATAL, UNEXP_MSG);
0238:                    throw new IOException("Unexpected SSL record, type: "
0239:                            + inputHeader[0]);
0240:
0241:                case ALRT:
0242:                    // An Alert record needs to be atleast 2 bytes of data
0243:                    if (inputData.length < 2) {
0244:                        throw new IOException("Bad alert length");
0245:                    }
0246:
0247:                    if ((inputData[0] == WARNING)
0248:                            && (inputData[1] == CLOSE_NTFY) && (type == APP)) {
0249:                        /*
0250:                         * We got a close_notify warning
0251:                         * Shutdown the connection.
0252:                         */
0253:                        shutdownConnection();
0254:                        return; // signal end of InputStream
0255:                    }
0256:
0257:                    if ((inputData[0] < WARNING) || (inputData[0] > FATAL)) {
0258:                        throw new IOException("Bad alert level");
0259:                    }
0260:
0261:                    throw new IOException("Alert (" + inputData[0] + ","
0262:                            + inputData[1] + ")");
0263:                }
0264:            }
0265:
0266:            /**
0267:             * Returns the next record read from the record layer (the 5-byte
0268:             * SSL record header is included). Set plainTextLength to length of the
0269:             * record or -1 for end of stream.
0270:             *
0271:             * @param block if true the method will not return until data is available,
0272:             *              or end of stream
0273:             *
0274:             * @return true if a record has been read
0275:             *
0276:             * @exception IOException if an I/O error occurs
0277:             */
0278:            private boolean rdRec(boolean block) throws IOException {
0279:                int b;
0280:
0281:                plainTextLength = 0;
0282:
0283:                if (!block && in.available() == 0) {
0284:                    return false;
0285:                }
0286:
0287:                /*
0288:                 * This method could have returned last time after reading the
0289:                 * part of the record, in that case headerBytesRead will not be 0.
0290:                 */
0291:                if (headerBytesRead == 0) {
0292:                    b = in.read(inputHeader, 0, 1);
0293:                    if (b == -1) {
0294:                        /*
0295:                         * Peer closed SSL connection without close_notify
0296:                         */
0297:                        plainTextLength = -1;
0298:                        return false;
0299:                    }
0300:
0301:                    headerBytesRead = 1;
0302:                    dataBytesRead = 0;
0303:                    dataLength = 0;
0304:                }
0305:
0306:                while (headerBytesRead < inputHeader.length) {
0307:                    if (!block && in.available() == 0) {
0308:                        return false;
0309:                    }
0310:
0311:                    b = in.read(inputHeader, headerBytesRead,
0312:                            inputHeader.length - headerBytesRead);
0313:                    if (b == -1) {
0314:                        throw new IOException(
0315:                                "SSL connection ended abnormally "
0316:                                        + "while reading record header");
0317:                    }
0318:
0319:                    headerBytesRead += b;
0320:                }
0321:
0322:                /*
0323:                 * This method could have returned last time after reading the
0324:                 * header but not all of the data, in that case dataLength
0325:                 * will not be 0.
0326:                 */
0327:                if (dataLength == 0) {
0328:                    // Check record type and version
0329:                    if ((inputHeader[0] < CCS) || (inputHeader[0] > APP)
0330:                            || (inputHeader[1] != (byte) (ver >>> 4))
0331:                            || (inputHeader[2] != (byte) (ver & 0x0f))) {
0332:                        alert(FATAL, UNEXP_MSG);
0333:                        throw new IOException("Bad record type ("
0334:                                + inputHeader[0] + ") or version ("
0335:                                + inputHeader[1] + "." + inputHeader[2] + ")");
0336:                    }
0337:
0338:                    dataLength = ((inputHeader[3] & 0xff) << 8)
0339:                            + (inputHeader[4] & 0xff);
0340:                    inputData = new byte[dataLength];
0341:                }
0342:
0343:                while (dataBytesRead < dataLength) {
0344:                    if (!block && in.available() == 0) {
0345:                        return false;
0346:                    }
0347:
0348:                    b = in.read(inputData, dataBytesRead, dataLength
0349:                            - dataBytesRead);
0350:                    if (b == -1) {
0351:                        throw new IOException(
0352:                                "SSL connection ended abnormally "
0353:                                        + "after reading record byte "
0354:                                        + (dataBytesRead + headerBytesRead));
0355:                    }
0356:
0357:                    dataBytesRead += b;
0358:                }
0359:
0360:                if (rActive == 1) {
0361:                    try {
0362:                        plainTextLength = decoder
0363:                                .decode(inputHeader, inputData);
0364:                    } catch (IOException e) {
0365:                        if (e.getMessage().compareTo("Bad MAC") == 0) {
0366:                            alert(FATAL, BAD_MAC);
0367:                        } else {
0368:                            throw e;
0369:                        }
0370:                    }
0371:                } else {
0372:                    plainTextLength = dataBytesRead;
0373:                }
0374:
0375:                if (inputHeader[0] == CCS) {
0376:                    rActive = 1;
0377:                }
0378:
0379:                // start with a new record header next time
0380:                headerBytesRead = 0;
0381:
0382:                return true;
0383:            }
0384:
0385:            /**
0386:             * Writes an SSL record to the underlying socket's output stream.
0387:             * 
0388:             * @param type record type (one of CCS, ALRT, HNDSHK or APP)
0389:             * @param buf byte array containing the record body (i.e. everything
0390:             * but the 5-byte header)
0391:             * @param off starting offset of the record body inside buf
0392:             * @param len length of the record body, the maximum is 2^14 +2048 as
0393:             *            defined by RFC 2246
0394:             *
0395:             * @exception IOException if an I/O error occurs.
0396:             */
0397:            /*
0398:             * REVISIT: We currently do not handle fragmentation and only 
0399:             * increment sequence numbers when encoding/decoding are 
0400:             * turned on. Is it necessary to maintain these counts for
0401:             * handshake messages as well???
0402:             */
0403:            void wrRec(byte type, byte[] buf, int off, int len)
0404:                    throws IOException {
0405:                byte[] rec;
0406:
0407:                if (shutdown) {
0408:                    throw new IOException("Server has shutdown the connection");
0409:                }
0410:
0411:                /*
0412:                 * Create a new byte array with room for the header and
0413:                 * fill the record header with type, version and length
0414:                 */
0415:                rec = new byte[len + 5];
0416:                rec[0] = type;
0417:                rec[1] = (byte) (ver >>> 4);
0418:                rec[2] = (byte) (ver & 0x0f);
0419:                rec[3] = (byte) (len >>> 8);
0420:                rec[4] = (byte) (len & 0xff);
0421:                // Fill the rest of the record
0422:                System.arraycopy(buf, off, rec, 5, len);
0423:                if (wActive == 1) {
0424:                    out.write(encoder.encode(rec));
0425:                } else {
0426:                    out.write(rec);
0427:                }
0428:                if (type == CCS)
0429:                    wActive = 1;
0430:            }
0431:
0432:            /**
0433:             * Sends an alert message of the specified level and type to the SSL peer.
0434:             * 
0435:             * @param level one of WARNING or FATAL)
0436:             * @param type one of CLOSE_NTFY, UNEXP_MSG, BAD_MAC, DECOMP_FAIL,
0437:             *             HNDSHK_FAIL, NO_CERT, BAD_CERT, UNSUP_CERT, CERT_REVKD,
0438:             *             CERT_EXPRD, CERT_UNKWN, BAD_PARAM
0439:             */
0440:            public void alert(byte level, byte type) {
0441:                byte[] tmp = new byte[2];
0442:                tmp[0] = level;
0443:                tmp[1] = type;
0444:
0445:                try {
0446:                    wrRec(ALRT, tmp, 0, 2);
0447:                } catch (IOException e) {
0448:                    // ignore, we do not want to step on the real error
0449:                }
0450:            }
0451:
0452:            /** Close input stream */
0453:            void closeInputStream() {
0454:                try {
0455:                    in.close();
0456:                } catch (IOException e) {
0457:                    // ignore
0458:                }
0459:            }
0460:
0461:            /** Close output stream */
0462:            void closeOutputStream() {
0463:                try {
0464:                    out.close();
0465:                } catch (IOException e) {
0466:                    // ignore
0467:                }
0468:            }
0469:
0470:            /**
0471:             * Send a close notify and shutdown the TCP connection if needed.
0472:             */
0473:            public void shutdownConnection() {
0474:                if (shutdown) {
0475:                    return;
0476:                }
0477:
0478:                alert(Record.WARNING, Record.CLOSE_NTFY);
0479:                shutdown = true;
0480:                closeOutputStream();
0481:                closeInputStream();
0482:            }
0483:        }
0484:
0485:        /**
0486:         * All kinds of data related to cipher suite like keys,
0487:         * digestes, etc.
0488:         */
0489:        class CipherSuiteData {
0490:            /** Client MAC secret. */
0491:            private byte[] clientMACSecret = null;
0492:            /** Server MAC secret. */
0493:            private byte[] serverMACSecret = null;
0494:            /** Client write key for bulk encryption. */
0495:            private byte[] clientKey = null;
0496:            /** Server write key for bulk encryption. */
0497:            private byte[] serverKey = null;
0498:            /** Client write IV for block encryption. */
0499:            private byte[] clientIV = null;
0500:            /** Server write IV for block encryption. */
0501:            private byte[] serverIV = null;
0502:            /** Clients bulk encryption secret key. */
0503:            private SecretKey clientBulkKey = null;
0504:            /** Servers bulk encryption secret key. */
0505:            private SecretKey serverBulkKey = null;
0506:            /** Length of the digest */
0507:            private int digestLength = 0;
0508:            /** Digest for encoding */
0509:            private MessageDigest encodeDigest = null;
0510:            /** Digest for decoding */
0511:            private MessageDigest decodeDigest = null;
0512:            /** Encode cipher */
0513:            private Cipher encodeCipher = null;
0514:            /** Decode cipher */
0515:            private Cipher decodeCipher = null;
0516:            /** Length of PAD1/PAD2 used in MACs. */
0517:            private int padLength = 0;
0518:            /** Cipher suite type */
0519:            private byte suiteType = 0;
0520:            /** Digest used for keys generation */
0521:            private MessageDigest md = null;
0522:            /** Digest used for keys generation */
0523:            private MessageDigest sd = null;
0524:            /** Block of generated keys */
0525:            private byte[] keyBlock = null;
0526:
0527:            /**
0528:             * Constructs CipherSuiteData object
0529:             * 
0530:             * @param suite negotiated cipher suite
0531:             *
0532:             * @exception Exception if the negotiated cipher suite involves an 
0533:             *                      unsupported hash or cipher algorithm
0534:             */
0535:            CipherSuiteData(byte suite) throws Exception {
0536:                suiteType = suite;
0537:
0538:                /* 
0539:                 * The actual size of our computed key block is the closest 
0540:                 * 16-byte multiple and depends on the choice of hashing
0541:                 * and encryption algorithms.
0542:                 */
0543:                int keyMaterial = 5;
0544:
0545:                switch (suite) {
0546:                case Handshake.ARCFOUR_128_MD5:
0547:                    keyMaterial = 16;
0548:                    // fall through
0549:                case Handshake.ARCFOUR_40_MD5:
0550:                    padLength = 48;
0551:                    encodeDigest = MessageDigest.getInstance("MD5");
0552:                    break;
0553:
0554:                case Handshake.ARCFOUR_128_SHA:
0555:                    keyMaterial = 16;
0556:                    padLength = 40;
0557:                    encodeDigest = MessageDigest.getInstance("SHA-1");
0558:                    break;
0559:
0560:                default:
0561:                    throw new Exception("Unsupported suite");
0562:                }
0563:
0564:                decodeDigest = (MessageDigest) encodeDigest.clone();
0565:                digestLength = encodeDigest.getDigestLength();
0566:
0567:                encodeCipher = Cipher.getInstance("ARC4");
0568:                decodeCipher = Cipher.getInstance("ARC4");
0569:
0570:                /* 
0571:                 * Key block size is the closest 16-byte multiple larger than
0572:                 *       2x(hash_size + key_material + IV_size)
0573:                 * int keyMaterial = CipherSuite.cipherList[
0574:                 * CipherSuite.suiteList[suite][2]][2];
0575:                 * int ivSize = CipherSuite.cipherList[
0576:                 *          CipherSuite.suiteList[suite][2]][4];
0577:                 */
0578:
0579:                int ivSize = 0; // stream ciphers do not use IVs
0580:
0581:                int blockSize = (digestLength + keyMaterial + ivSize) << 1;
0582:                blockSize = ((blockSize + 15) >>> 4) << 4;
0583:                keyBlock = new byte[blockSize];
0584:
0585:                clientMACSecret = new byte[digestLength];
0586:                serverMACSecret = new byte[digestLength];
0587:                clientKey = new byte[keyMaterial];
0588:                serverKey = new byte[keyMaterial];
0589:                clientIV = new byte[ivSize];
0590:                serverIV = new byte[ivSize];
0591:
0592:                md = MessageDigest.getInstance("MD5");
0593:                sd = MessageDigest.getInstance("SHA-1");
0594:            }
0595:
0596:            /**
0597:             * Chops up a master secret into the client and server MAC secrets, 
0598:             * bulk encryption keys and IVs.
0599:             * 
0600:             * @param clientRand 32-byte random value chosen by the client
0601:             * @param serverRand 32-byte random value chosen by the server
0602:             * @param masterSecret master secret resulting from the key exchange
0603:             *
0604:             * @exception GeneralSecurityException thrown in case of failure
0605:             */
0606:            void generateKeys(byte[] clientRand, byte[] serverRand,
0607:                    byte[] masterSecret) throws GeneralSecurityException {
0608:                generateKeysBlock(clientRand, serverRand, masterSecret);
0609:                chopKeysBlock(clientRand, serverRand, masterSecret);
0610:                keyBlock = null;
0611:                md = null;
0612:                sd = null;
0613:            }
0614:
0615:            /**
0616:             * Get client MAC secret
0617:             * @return client MAC secret
0618:             */
0619:            byte[] getClientMACSecret() {
0620:                return clientMACSecret;
0621:            }
0622:
0623:            /**
0624:             * Get server MAC secret
0625:             * @return server MAC secret
0626:             */
0627:            byte[] getServerMACSecret() {
0628:                return serverMACSecret;
0629:            }
0630:
0631:            /**
0632:             * Get client bulk key
0633:             * @return client bulk key
0634:             */
0635:            SecretKey getClientBulkKey() {
0636:                return clientBulkKey;
0637:            }
0638:
0639:            /**
0640:             * Get server bulk key
0641:             * @return server bulk key
0642:             */
0643:            SecretKey getServerBulkKey() {
0644:                return serverBulkKey;
0645:            }
0646:
0647:            /**
0648:             * Get digest used for encoding 
0649:             * @return encode digest
0650:             */
0651:            MessageDigest getEncodeDigest() {
0652:                return encodeDigest;
0653:            }
0654:
0655:            /**
0656:             * Get digest used for decoding
0657:             * @return decode digest
0658:             */
0659:            MessageDigest getDecodeDigest() {
0660:                return decodeDigest;
0661:            }
0662:
0663:            /**
0664:             * Get cipher used for encoding
0665:             * @return encode cipher
0666:             */
0667:            Cipher getEncodeCipher() {
0668:                return encodeCipher;
0669:            }
0670:
0671:            /**
0672:             * Get cipher used for decoding
0673:             * @return decode cipher
0674:             */
0675:            Cipher getDecodeCipher() {
0676:                return decodeCipher;
0677:            }
0678:
0679:            /**
0680:             * Get pad length used for MAC computation 
0681:             * @return pad length
0682:             */
0683:            int getPadLength() {
0684:                return padLength;
0685:            }
0686:
0687:            /**
0688:             * Generates keys block
0689:             * 
0690:             * @param clientRand 32-byte random value chosen by the client
0691:             * @param serverRand 32-byte random value chosen by the server
0692:             * @param masterSecret master secret resulting from the key exchange
0693:             *
0694:             * @exception GeneralSecurityException thrown in case of failure
0695:             */
0696:            private void generateKeysBlock(byte[] clientRand,
0697:                    byte[] serverRand, byte[] masterSecret)
0698:                    throws GeneralSecurityException {
0699:                /* 
0700:                 * The following should suffice to generate a total of
0701:                 * 16*7 = 112 bytes of key material. 3DES_SHA requires
0702:                 * 2*(20 + 24 + 8) = 104 bytes.
0703:                 */
0704:                byte[] expansion[] = { { 0x41 }, // 'A'
0705:                        { 0x42, 0x42 }, // 'BB'
0706:                        { 0x43, 0x43, 0x43 }, // 'CCC'
0707:                        { 0x44, 0x44, 0x44, 0x44 }, // 'DDDD'
0708:                        { 0x45, 0x45, 0x45, 0x45, 0x45 }, // 'EEEEE'
0709:                        { 0x46, 0x46, 0x46, 0x46, 0x46, 0x46 }, // 'FFFFFF'
0710:                        { 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47 }, // 'GGGGGGG'
0711:                };
0712:
0713:                /*
0714:                 * key_block =
0715:                 *    MD5(master + SHA('A' + master +
0716:                 *                     ServerHello.random + ClientHello.random)) +
0717:                 *    MD5(master + SHA('BB' + master +
0718:                 *                     ServerHello.random + ClientHello.random)) +
0719:                 *    MD5(master + SHA('CCC' + master +
0720:                 *                     ServerHello.random + ClientHello.random)) +
0721:                 *    [..]
0722:                 *
0723:                 * We set 
0724:                 *     subExp = master + ServerHello.random + ClientHello.random
0725:                 */
0726:                byte[] blockSubExp = new byte[masterSecret.length
0727:                        + serverRand.length + clientRand.length];
0728:                int offset = 0;
0729:                System.arraycopy(masterSecret, 0, blockSubExp, offset,
0730:                        masterSecret.length);
0731:                offset += masterSecret.length;
0732:                System.arraycopy(serverRand, 0, blockSubExp, offset,
0733:                        serverRand.length);
0734:                offset += serverRand.length;
0735:                System.arraycopy(clientRand, 0, blockSubExp, offset,
0736:                        clientRand.length);
0737:
0738:                for (int i = 0; i < (keyBlock.length >>> 4); i++) {
0739:                    md.update(masterSecret, 0, masterSecret.length);
0740:                    sd.update(expansion[i], 0, expansion[i].length);
0741:                    byte[] res = new byte[20];
0742:                    sd.update(blockSubExp, 0, blockSubExp.length);
0743:                    sd.digest(res, 0, res.length);
0744:                    md.update(res, 0, 20);
0745:                    md.digest(keyBlock, i << 4, md.getDigestLength());
0746:                }
0747:            }
0748:
0749:            /**
0750:             * Extract keys form keys block
0751:             * 
0752:             * @param clientRand 32-byte random value chosen by the client
0753:             * @param serverRand 32-byte random value chosen by the server
0754:             * @param masterSecret master secret resulting from the key exchange
0755:             *
0756:             * @exception GeneralSecurityException thrown in case of failure
0757:             */
0758:            private void chopKeysBlock(byte[] clientRand, byte[] serverRand,
0759:                    byte[] masterSecret) throws GeneralSecurityException {
0760:                int offset = 0;
0761:
0762:                System.arraycopy(keyBlock, 0, clientMACSecret, 0,
0763:                        clientMACSecret.length);
0764:                offset += clientMACSecret.length;
0765:
0766:                System.arraycopy(keyBlock, offset, serverMACSecret, 0,
0767:                        serverMACSecret.length);
0768:                offset += serverMACSecret.length;
0769:
0770:                System.arraycopy(keyBlock, offset, clientKey, 0,
0771:                        clientKey.length);
0772:                offset += clientKey.length;
0773:
0774:                System.arraycopy(keyBlock, offset, serverKey, 0,
0775:                        serverKey.length);
0776:                offset += serverKey.length;
0777:
0778:                if (suiteType == Handshake.ARCFOUR_128_MD5
0779:                        || suiteType == Handshake.ARCFOUR_128_SHA) {
0780:                    /*
0781:                     * NOTE: We know ivSze is always zero for ARCFOUR cipher suites
0782:                     * so this is commented out. It wasn't removed in case we need
0783:                     * to add support for DES or another block cipher.
0784:                     * if (ivSize != 0) {
0785:                     *     // bulk encryption uses a block cipher, so initialize IVs
0786:                     *     System.arraycopy(keyBlock, 
0787:                     *         2*(clientMACSecret.length + clientKey.length), 
0788:                     *         clientIV, 0, ivSize);
0789:                     *     System.arraycopy(keyBlock, 
0790:                     *         2*(clientMACSecret.length + clientKey.length) 
0791:                     *         + ivSize, serverIV, 0, ivSize);
0792:                     * }
0793:                     */
0794:                } else {
0795:                    /* 
0796:                     * IMPL_NOTE: The only other option is ARCFOUR_40_MD5
0797:                     * 
0798:                     * Expand the keys for exportable cipher suites
0799:                     *  final_client_write_key = MD5(client_write_key +
0800:                     *                                ClientHello.random +
0801:                     *                               ServerHello.random);
0802:                     *  final_server_write_key = MD5(server_write_key +
0803:                     *                               ServerHello.random +
0804:                     *                               ClientHello.random);
0805:                     */
0806:                    byte[] res = new byte[16];
0807:                    md.update(clientKey, 0, clientKey.length);
0808:                    md.update(clientRand, 0, clientRand.length);
0809:                    md.update(serverRand, 0, serverRand.length);
0810:                    md.digest(res, 0, res.length);
0811:
0812:                    /* 
0813:                     * NOTE: For both ARCFOUR_128_MD5 and ARCFOUR_40_MD5, 
0814:                     * expanded key is 16
0815:                     */
0816:                    byte[] fcKey = new byte[16];
0817:                    System.arraycopy(res, 0, fcKey, 0, fcKey.length);
0818:
0819:                    md.update(serverKey, 0, serverKey.length);
0820:                    md.update(serverRand, 0, serverRand.length);
0821:                    md.update(clientRand, 0, clientRand.length);
0822:                    md.digest(res, 0, res.length);
0823:                    byte[] fserverKey = new byte[fcKey.length];
0824:                    System.arraycopy(res, 0, fserverKey, 0, fserverKey.length);
0825:
0826:                    clientKey = fcKey;
0827:                    serverKey = fserverKey;
0828:
0829:                    /* 
0830:                     * ... and compute IVs in a special way
0831:                     * client_write_IV = MD5(ClientHello.random + ServerHello.random)
0832:                     * server_write_IV = MD5(ServerHello.random + ClientHello.random)
0833:                     * 
0834:                     * NOTE: We know for the chosen ciphersuites, ivSize is zero
0835:                     * so this code is commented out for now
0836:                        if (ivSize != 0) {
0837:                            md.update(clientRand, (short) 0, 
0838:                                (short) clientRand.length);
0839:                            md.doFinal(serverRand, (short) 0, 
0840:                                (short) serverRand.length, res, (short) 0);
0841:                            System.arraycopy(res, 0, clientIV, 0, ivSize);
0842:                            
0843:                            md.update(serverRand, (short) 0, 
0844:                                (short) serverRand.length);
0845:                            md.doFinal(clientRand, (short) 0, 
0846:                                (short) clientRand.length, res, (short) 0);
0847:                            System.arraycopy(res, 0, serverIV, 0, ivSize);
0848:                    }
0849:                     */
0850:                }
0851:
0852:                /*
0853:                 * Now initialize the ciphers and keys. FOr now this is always
0854:                 * ARCFOUR and we comment out support for other ciphers.
0855:                 */
0856:                clientBulkKey = new SecretKey(clientKey, 0, clientKey.length,
0857:                        "ARC4");
0858:                serverBulkKey = new SecretKey(serverKey, 0, serverKey.length,
0859:                        "ARC4");
0860:            }
0861:        }
0862:
0863:        /**
0864:         * Implements MAC computation
0865:         */
0866:        class MAC {
0867:            /** 
0868:             * PAD1 is a 48-byte array filled with 0x36
0869:             */
0870:            static final byte[] PAD1 = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0871:                    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0872:                    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0873:                    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0874:                    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0875:                    0x36, 0x36 };
0876:
0877:            /** 
0878:             * PAD2 is a 48-byte array filled with 0x5c
0879:             */
0880:            static final byte[] PAD2 = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0881:                    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0882:                    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0883:                    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0884:                    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0885:                    0x5c, 0x5c };
0886:
0887:            /**
0888:             * Data used for MAC computation
0889:             */
0890:            /** MAC secret */
0891:            protected byte[] macSecret = null;
0892:            /** Message digest */
0893:            protected MessageDigest digest = null;
0894:            /** Message digest length */
0895:            protected int digestLength = 0;
0896:            /** pad length */
0897:            protected int padLength = 0;
0898:            /** Write sequence number */
0899:            private long sequenceNumber = 0;
0900:
0901:            /** 
0902:             * Computes the MAC for an SSLCompressed structure.
0903:             * 
0904:             * @param type SSL record type of the SSLCompressed structure
0905:             * @param buf byte array containing the SSLCompressed fragment
0906:             * @param offset starting offset of the fragment in buf
0907:             * @param length length of the fragment
0908:             *
0909:             * @return a byte array containing the MAC
0910:             */
0911:            byte[] getMAC(byte type, byte[] buf, int offset, int length) {
0912:                /* 
0913:                 * MAC = hash(MAC_secret + PAD2 +
0914:                 *    hash(MAC_secret + PAD1 + seq_num + type + len +
0915:                 *         compressed_fragment));
0916:                 */
0917:
0918:                // Compute the inner hash first
0919:                byte[] byteArray = null;
0920:                digest.update(macSecret, 0, macSecret.length);
0921:                digest.update(PAD1, 0, padLength);
0922:                byteArray = Utils.longToBytes(sequenceNumber);
0923:                digest.update(byteArray, 0, byteArray.length);
0924:
0925:                byteArray = new byte[3];
0926:                byteArray[0] = type;
0927:                byteArray[1] = (byte) (length >>> 8);
0928:                byteArray[2] = (byte) (length & 0xff);
0929:                digest.update(byteArray, 0, byteArray.length);
0930:                byte[] innerHash = new byte[digest.getDigestLength()];
0931:                digest.update(buf, offset, length);
0932:                try {
0933:                    digest.digest(innerHash, 0, innerHash.length);
0934:                } catch (DigestException e) {
0935:                    // Ignore this exception, it should never happen
0936:                }
0937:
0938:                // Now, the outer hash
0939:                digest.update(macSecret, 0, macSecret.length);
0940:                digest.update(PAD2, 0, padLength);
0941:                byte[] mac = new byte[innerHash.length];
0942:                digest.update(innerHash, 0, innerHash.length);
0943:                try {
0944:                    digest.digest(mac, 0, mac.length);
0945:                } catch (DigestException e) {
0946:                    // Ignore this exception, it should never happen
0947:                }
0948:
0949:                return mac;
0950:            }
0951:
0952:            /**
0953:             * Increments write sequence number
0954:             * @exception IOException if the sequence numbers rolls around
0955:             */
0956:            void incrementSequenceNumber() throws IOException {
0957:                if (++sequenceNumber == (long) 0)
0958:                    throw new IOException("Sequence number rolled over");
0959:            }
0960:        }
0961:
0962:        /**
0963:         * Implements record's encoding
0964:         */
0965:        class RecordEncoder extends MAC {
0966:            /** Cipher used for encryption */
0967:            private Cipher cipher;
0968:
0969:            /**
0970:             * Constructs RecordEncoder object
0971:             * 
0972:             * @param dgst digest for MAC computation
0973:             * @param secret MAC secret
0974:             * @param padLen padding length
0975:             * @param cphr cipher used for encoding
0976:             */
0977:            RecordEncoder(MessageDigest dgst, byte[] secret, int padLen,
0978:                    Cipher cphr) {
0979:                macSecret = secret;
0980:                digest = dgst;
0981:                digestLength = digest.getDigestLength();
0982:                padLength = padLen;
0983:                cipher = cphr;
0984:            }
0985:
0986:            /**
0987:             * Converts a byte array containing an SSLPlaintext structure
0988:             * to the corresponding SSLCiphertext structure. The process 
0989:             * typically involves the addition of a MAC followed by 
0990:             * encryption.
0991:             * 
0992:             * @param plainText byte array containing SSLPlaintext
0993:             * @return the number of bytes written to the OutputStream
0994:             *
0995:             * @exception IOException if a problem is encountered during
0996:             * encryption
0997:             */
0998:            byte[] encode(byte[] plainText) throws IOException {
0999:                /*
1000:                 * Since we only support NULL compression, SSLPlaintext
1001:                 * the same as SSLCompressed.
1002:                 */
1003:                byte[] fragAndMAC = null; // fragment plus MAC
1004:
1005:                if (digest != null) {
1006:                    fragAndMAC = new byte[plainText.length - 5 + digestLength];
1007:                    System.arraycopy(plainText, 5, fragAndMAC, 0,
1008:                            plainText.length - 5);
1009:                    byte[] mac = getMAC(plainText[0], plainText, 5,
1010:                            plainText.length - 5);
1011:                    System.arraycopy(mac, 0, fragAndMAC,
1012:                            (plainText.length - 5), digestLength);
1013:                } else {
1014:                    fragAndMAC = new byte[plainText.length - 5];
1015:                    System.arraycopy(plainText, 5, fragAndMAC, 0,
1016:                            plainText.length - 5);
1017:                }
1018:
1019:                // ... now we need to encrypt fragAndMAC and possibly update IVs
1020:                byte[] efragAndMAC = null;
1021:                if (cipher != null) {
1022:                    try {
1023:                        /*
1024:                         * NOTE: For now, we always have a stream cipher so this
1025:                         * is commented out.
1026:                         */
1027:                        // We have a stream cipher
1028:                        efragAndMAC = fragAndMAC;
1029:                        cipher.update(fragAndMAC, 0, fragAndMAC.length,
1030:                                efragAndMAC, 0);
1031:                    } catch (Exception e) {
1032:                        throw new IOException("Encode caught " + e);
1033:                    }
1034:                } else {
1035:                    // Cipher algorithm is NULL
1036:                    efragAndMAC = fragAndMAC;
1037:                }
1038:
1039:                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1040:                    Logging.report(Logging.INFORMATION,
1041:                            LogChannels.LC_SECURITY, "efragAndMAC: "
1042:                                    + Utils.hexEncode(efragAndMAC));
1043:                }
1044:
1045:                byte[] record = new byte[efragAndMAC.length + 5];
1046:                System.arraycopy(plainText, 0, record, 0, 3);
1047:                record[3] = (byte) (efragAndMAC.length >>> 8);
1048:                record[4] = (byte) (efragAndMAC.length & 0xff);
1049:                System.arraycopy(efragAndMAC, 0, record, 5, efragAndMAC.length);
1050:
1051:                // We have encoded one more record, increment seq number
1052:                incrementSequenceNumber();
1053:
1054:                return record;
1055:            }
1056:        }
1057:
1058:        /**
1059:         * Implements record's decoding
1060:         */
1061:        class RecordDecoder extends MAC {
1062:            /** Cipher used for decryption */
1063:            private Cipher cipher;
1064:
1065:            /**
1066:             * Constructs RecordDecoder object
1067:             * 
1068:             * @param dgst digest for MAC computation
1069:             * @param secret MAC secret
1070:             * @param padLen padding length
1071:             * @param cphr cipher used for decoding
1072:             */
1073:            RecordDecoder(MessageDigest dgst, byte[] secret, int padLen,
1074:                    Cipher cphr) {
1075:                macSecret = secret;
1076:                digest = dgst;
1077:                digestLength = digest.getDigestLength();
1078:                padLength = padLen;
1079:                cipher = cphr;
1080:            }
1081:
1082:            /**
1083:             * Converts a byte array containing an SSLCiphertext structure
1084:             * to the corresponding SSLPlaintext structure. The process
1085:             * typically involves decryption followed by MAC verification
1086:             * and MAC stripping.
1087:             * @param recordHeader record header
1088:             * @param recordData record data
1089:             * @return Length of the decrypted data in the input buffer.
1090:             * 
1091:             * @exception IOException if a problem is encountered during decryption
1092:             *                        or MAC verification
1093:             */
1094:            int decode(byte[] recordHeader, byte[] recordData)
1095:                    throws IOException {
1096:                if (cipher != null) {
1097:                    // Cipher algorithm is not NULL (ctxt needs to be decrypted)
1098:
1099:                    try {
1100:                        /*
1101:                         * NOTE: For now, we only have ARCFOUR, a stream cipher
1102:                         * so there is no IV or padding that a block cipher has.
1103:                         *
1104:                         * Otherwise we would have to find the plaintext offset
1105:                         * after we decrypt.
1106:                         */
1107:
1108:                        // We have a stream cipher (NOTE: assuming CLIENT role)
1109:                        // We can decode in place w/o using additional memory
1110:                        cipher.update(recordData, 0, recordData.length,
1111:                                recordData, 0);
1112:                    } catch (Exception e) {
1113:                        throw new IOException("Decode caught " + e);
1114:                    }
1115:                }
1116:
1117:                int length = recordData.length - digestLength;
1118:                if (digest != null) {
1119:                    byte[] expMAC = null; // expected MAC
1120:                    expMAC = getMAC(recordHeader[0], recordData, 0, length);
1121:                    if (!Utils.byteMatch(expMAC, 0, recordData, length,
1122:                            digestLength)) {
1123:                        throw new IOException("Bad MAC");
1124:                    }
1125:                }
1126:
1127:                incrementSequenceNumber();
1128:
1129:                return length;
1130:            }
1131:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.