001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.xnet.provider.jsse;
022:
023: import java.security.GeneralSecurityException;
024: import java.security.MessageDigest;
025: import java.util.Arrays;
026: import javax.crypto.Cipher;
027: import javax.crypto.spec.IvParameterSpec;
028: import javax.crypto.spec.SecretKeySpec;
029: import javax.net.ssl.SSLProtocolException;
030:
031: /**
032: * This class incapsulates the operating environment of the SSL v3
033: * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides
034: * relating encryption/decryption functionality.
035: * The work functionality is based on the security
036: * parameters negotiated during the handshake.
037: */
038: public class ConnectionStateSSLv3 extends ConnectionState {
039:
040: // digest to create and check the message integrity info
041: private final MessageDigest messageDigest;
042: private final byte[] mac_write_secret;
043: private final byte[] mac_read_secret;
044:
045: // paddings
046: private final byte[] pad_1;
047: private final byte[] pad_2;
048: // array will hold the part of the MAC material:
049: // length of 3 == 1(SSLCompressed.type) + 2(SSLCompressed.length)
050: // (more on SSLv3 MAC computation and payload protection see
051: // SSL v3 specification, p. 5.2.3)
052: private final byte[] mac_material_part = new byte[3];
053:
054: /**
055: * Creates the instance of SSL v3 Connection State. All of the
056: * security parameters are provided by session object.
057: * @param session: the sessin object which incapsulates
058: * all of the security parameters established by handshake protocol.
059: * The key calculation for the state is done according
060: * to the SSL v3 Protocol specification.
061: * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)
062: */
063: protected ConnectionStateSSLv3(SSLSessionImpl session) {
064: try {
065: CipherSuite cipherSuite = session.cipherSuite;
066:
067: boolean is_exportabe = cipherSuite.isExportable();
068: hash_size = cipherSuite.getMACLength();
069: int key_size = (is_exportabe) ? cipherSuite.keyMaterial
070: : cipherSuite.expandedKeyMaterial;
071: int iv_size = cipherSuite.getBlockSize();
072:
073: String algName = cipherSuite.getBulkEncryptionAlgorithm();
074: String hashName = cipherSuite.getHashName();
075: if (logger != null) {
076: logger.println("ConnectionStateSSLv3.create:");
077: logger.println(" cipher suite name: "
078: + session.getCipherSuite());
079: logger.println(" encryption alg name: " + algName);
080: logger.println(" hash alg name: " + hashName);
081: logger.println(" hash size: " + hash_size);
082: logger.println(" block size: " + iv_size);
083: logger.println(" IV size (== block size):" + iv_size);
084: logger.println(" key size: " + key_size);
085: }
086:
087: byte[] clientRandom = session.clientRandom;
088: byte[] serverRandom = session.serverRandom;
089: // so we need PRF value of size of
090: // 2*hash_size + 2*key_size + 2*iv_size
091: byte[] key_block = new byte[2 * hash_size + 2 * key_size
092: + 2 * iv_size];
093: byte[] seed = new byte[clientRandom.length
094: + serverRandom.length];
095: System.arraycopy(serverRandom, 0, seed, 0,
096: serverRandom.length);
097: System.arraycopy(clientRandom, 0, seed,
098: serverRandom.length, clientRandom.length);
099:
100: PRF
101: .computePRF_SSLv3(key_block, session.master_secret,
102: seed);
103:
104: byte[] client_mac_secret = new byte[hash_size];
105: byte[] server_mac_secret = new byte[hash_size];
106: byte[] client_key = new byte[key_size];
107: byte[] server_key = new byte[key_size];
108:
109: boolean is_client = !session.isServer;
110:
111: is_block_cipher = (iv_size > 0);
112:
113: System.arraycopy(key_block, 0, client_mac_secret, 0,
114: hash_size);
115: System.arraycopy(key_block, hash_size, server_mac_secret,
116: 0, hash_size);
117: System.arraycopy(key_block, 2 * hash_size, client_key, 0,
118: key_size);
119: System.arraycopy(key_block, 2 * hash_size + key_size,
120: server_key, 0, key_size);
121:
122: IvParameterSpec clientIV = null;
123: IvParameterSpec serverIV = null;
124:
125: if (is_exportabe) {
126: if (logger != null) {
127: logger
128: .println("ConnectionStateSSLv3: is_exportable");
129: }
130:
131: MessageDigest md5 = MessageDigest.getInstance("MD5");
132: md5.update(client_key);
133: md5.update(clientRandom);
134: md5.update(serverRandom);
135: client_key = md5.digest();
136:
137: md5.update(server_key);
138: md5.update(serverRandom);
139: md5.update(clientRandom);
140: server_key = md5.digest();
141:
142: key_size = cipherSuite.expandedKeyMaterial;
143:
144: if (is_block_cipher) {
145: md5.update(clientRandom);
146: md5.update(serverRandom);
147: clientIV = new IvParameterSpec(md5.digest(), 0,
148: iv_size);
149: md5.update(serverRandom);
150: md5.update(clientRandom);
151: serverIV = new IvParameterSpec(md5.digest(), 0,
152: iv_size);
153: }
154: } else if (is_block_cipher) {
155: clientIV = new IvParameterSpec(key_block, 2 * hash_size
156: + 2 * key_size, iv_size);
157: serverIV = new IvParameterSpec(key_block, 2 * hash_size
158: + 2 * key_size + iv_size, iv_size);
159: }
160:
161: if (logger != null) {
162: logger.println("is exportable: " + is_exportabe);
163: logger.println("master_secret");
164: logger.print(session.master_secret);
165: logger.println("client_random");
166: logger.print(clientRandom);
167: logger.println("server_random");
168: logger.print(serverRandom);
169: //logger.println("key_block");
170: //logger.print(key_block);
171: logger.println("client_mac_secret");
172: logger.print(client_mac_secret);
173: logger.println("server_mac_secret");
174: logger.print(server_mac_secret);
175: logger.println("client_key");
176: logger.print(client_key, 0, key_size);
177: logger.println("server_key");
178: logger.print(server_key, 0, key_size);
179: if (clientIV != null) {
180: logger.println("client_iv");
181: logger.print(clientIV.getIV());
182: logger.println("server_iv");
183: logger.print(serverIV.getIV());
184: } else {
185: logger.println("no IV.");
186: }
187: }
188: encCipher = Cipher.getInstance(algName);
189: decCipher = Cipher.getInstance(algName);
190: messageDigest = MessageDigest.getInstance(hashName);
191: if (is_client) { // client side
192: encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(
193: client_key, 0, key_size, algName), clientIV);
194: decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(
195: server_key, 0, key_size, algName), serverIV);
196: mac_write_secret = client_mac_secret;
197: mac_read_secret = server_mac_secret;
198: } else { // server side
199: encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(
200: server_key, 0, key_size, algName), serverIV);
201: decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(
202: client_key, 0, key_size, algName), clientIV);
203: mac_write_secret = server_mac_secret;
204: mac_read_secret = client_mac_secret;
205: }
206: if (hashName.equals("MD5")) {
207: pad_1 = SSLv3Constants.MD5pad1;
208: pad_2 = SSLv3Constants.MD5pad2;
209: } else {
210: pad_1 = SSLv3Constants.SHApad1;
211: pad_2 = SSLv3Constants.SHApad2;
212: }
213: } catch (Exception e) {
214: e.printStackTrace();
215: throw new AlertException(
216: AlertProtocol.INTERNAL_ERROR,
217: new SSLProtocolException(
218: "Error during computation of security parameters"));
219: }
220: }
221:
222: /**
223: * Creates the GenericStreamCipher or GenericBlockCipher
224: * data structure for specified data of specified type.
225: * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
226: */
227: protected byte[] encrypt(byte type, byte[] fragment, int offset,
228: int len) {
229: try {
230: int content_mac_length = len + hash_size;
231: int padding_length = is_block_cipher ? padding_length = ((8 - (++content_mac_length & 0x07)) & 0x07)
232: : 0;
233: byte[] res = new byte[content_mac_length + padding_length];
234: System.arraycopy(fragment, offset, res, 0, len);
235:
236: mac_material_part[0] = type;
237: mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8);
238: mac_material_part[2] = (byte) (0x0000FF & len);
239:
240: messageDigest.update(mac_write_secret);
241: messageDigest.update(pad_1);
242: messageDigest.update(write_seq_num);
243: messageDigest.update(mac_material_part);
244: messageDigest.update(fragment, offset, len);
245: byte[] digest = messageDigest.digest();
246: messageDigest.update(mac_write_secret);
247: messageDigest.update(pad_2);
248: messageDigest.update(digest);
249: digest = messageDigest.digest();
250: System.arraycopy(digest, 0, res, len, hash_size);
251:
252: //if (logger != null) {
253: // logger.println("MAC Material:");
254: // logger.print(write_seq_num);
255: // logger.print(mac_material_header);
256: // logger.print(fragment, offset, len);
257: //}
258:
259: if (is_block_cipher) {
260: // do padding:
261: Arrays.fill(res, content_mac_length - 1, res.length,
262: (byte) (padding_length));
263: }
264: if (logger != null) {
265: logger
266: .println("SSLRecordProtocol.encrypt: "
267: + (is_block_cipher ? "GenericBlockCipher with padding["
268: + padding_length + "]:"
269: : "GenericStreamCipher:"));
270: logger.print(res);
271: }
272: byte[] rez = new byte[encCipher.getOutputSize(res.length)];
273: encCipher.update(res, 0, res.length, rez);
274: incSequenceNumber(write_seq_num);
275: return rez;
276: } catch (GeneralSecurityException e) {
277: e.printStackTrace();
278: throw new AlertException(AlertProtocol.INTERNAL_ERROR,
279: new SSLProtocolException(
280: "Error during the encryption"));
281: }
282: }
283:
284: /**
285: * Retrieves the fragment of the Plaintext structure of
286: * the specified type from the provided data.
287: * @throws AlertException if alert was occured.
288: */
289: protected byte[] decrypt(byte type, byte[] fragment, int offset,
290: int len) {
291: // plain data of the Generic[Stream|Block]Cipher structure
292: byte[] data = decCipher.update(fragment, offset, len);
293: // the 'content' part of the structure
294: byte[] content;
295: if (is_block_cipher) {
296: // check padding
297: int padding_length = data[data.length - 1];
298: for (int i = 0; i < padding_length; i++) {
299: if (data[data.length - 2 - i] != padding_length) {
300: throw new AlertException(
301: AlertProtocol.DECRYPTION_FAILED,
302: new SSLProtocolException(
303: "Received message has bad padding"));
304: }
305: }
306: content = new byte[data.length - hash_size - padding_length
307: - 1];
308: } else {
309: content = new byte[data.length - hash_size];
310: }
311:
312: byte[] mac_value;
313:
314: mac_material_part[0] = type;
315: mac_material_part[1] = (byte) ((0x00FF00 & content.length) >> 8);
316: mac_material_part[2] = (byte) (0x0000FF & content.length);
317:
318: messageDigest.update(mac_read_secret);
319: messageDigest.update(pad_1);
320: messageDigest.update(read_seq_num);
321: messageDigest.update(mac_material_part);
322: messageDigest.update(data, 0, content.length);
323: mac_value = messageDigest.digest();
324: messageDigest.update(mac_read_secret);
325: messageDigest.update(pad_2);
326: messageDigest.update(mac_value);
327: mac_value = messageDigest.digest();
328:
329: if (logger != null) {
330: logger.println("Decrypted:");
331: logger.print(data);
332: //logger.println("MAC Material:");
333: //logger.print(read_seq_num);
334: //logger.print(mac_material_header);
335: //logger.print(data, 0, content.length);
336: logger.println("Expected mac value:");
337: logger.print(mac_value);
338: }
339: // checking the mac value
340: for (int i = 0; i < hash_size; i++) {
341: if (mac_value[i] != data[i + content.length]) {
342: throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
343: new SSLProtocolException("Bad record MAC"));
344: }
345: }
346: System.arraycopy(data, 0, content, 0, content.length);
347: incSequenceNumber(read_seq_num);
348: return content;
349: }
350:
351: /**
352: * Shutdownes the protocol. It will be impossiblke to use the instance
353: * after the calling of this method.
354: */
355: protected void shutdown() {
356: Arrays.fill(mac_write_secret, (byte) 0);
357: Arrays.fill(mac_read_secret, (byte) 0);
358: super.shutdown();
359: }
360: }
|