| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.xnet.provider.jsse; |
| |
| import java.security.GeneralSecurityException; |
| import java.security.MessageDigest; |
| import java.util.Arrays; |
| import javax.crypto.Cipher; |
| import javax.crypto.NullCipher; |
| import javax.crypto.spec.IvParameterSpec; |
| import javax.crypto.spec.SecretKeySpec; |
| import javax.net.ssl.SSLProtocolException; |
| |
| /** |
| * This class encapsulates the operating environment of the SSL v3 |
| * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides |
| * relating encryption/decryption functionality. |
| * The work functionality is based on the security |
| * parameters negotiated during the handshake. |
| */ |
| public class ConnectionStateSSLv3 extends ConnectionState { |
| |
| // digest to create and check the message integrity info |
| private final MessageDigest messageDigest; |
| private final byte[] mac_write_secret; |
| private final byte[] mac_read_secret; |
| |
| // paddings |
| private final byte[] pad_1; |
| private final byte[] pad_2; |
| // array will hold the part of the MAC material: |
| // length of 3 == 1(SSLCompressed.type) + 2(SSLCompressed.length) |
| // (more on SSLv3 MAC computation and payload protection see |
| // SSL v3 specification, p. 5.2.3) |
| private final byte[] mac_material_part = new byte[3]; |
| |
| /** |
| * Creates the instance of SSL v3 Connection State. All of the |
| * security parameters are provided by session object. |
| * @param session: the sessin object which incapsulates |
| * all of the security parameters established by handshake protocol. |
| * The key calculation for the state is done according |
| * to the SSL v3 Protocol specification. |
| * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt) |
| */ |
| protected ConnectionStateSSLv3(SSLSessionImpl session) { |
| try { |
| CipherSuite cipherSuite = session.cipherSuite; |
| |
| boolean is_exportabe = cipherSuite.isExportable(); |
| hash_size = cipherSuite.getMACLength(); |
| int key_size = (is_exportabe) |
| ? cipherSuite.keyMaterial |
| : cipherSuite.expandedKeyMaterial; |
| int iv_size = cipherSuite.ivSize; |
| block_size = cipherSuite.getBlockSize(); |
| |
| String algName = cipherSuite.getBulkEncryptionAlgorithm(); |
| String hashName = cipherSuite.getHashName(); |
| if (logger != null) { |
| logger.println("ConnectionStateSSLv3.create:"); |
| logger.println(" cipher suite name: " |
| + session.getCipherSuite()); |
| logger.println(" encryption alg name: " + algName); |
| logger.println(" hash alg name: " + hashName); |
| logger.println(" hash size: " + hash_size); |
| logger.println(" block size: " + block_size); |
| logger.println(" IV size:" + iv_size); |
| logger.println(" key size: " + key_size); |
| } |
| |
| byte[] clientRandom = session.clientRandom; |
| byte[] serverRandom = session.serverRandom; |
| // so we need PRF value of size of |
| // 2*hash_size + 2*key_size + 2*iv_size |
| byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size]; |
| byte[] seed = new byte[clientRandom.length + serverRandom.length]; |
| System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length); |
| System.arraycopy(clientRandom, 0, seed, serverRandom.length, |
| clientRandom.length); |
| |
| PRF.computePRF_SSLv3(key_block, session.master_secret, seed); |
| |
| byte[] client_mac_secret = new byte[hash_size]; |
| byte[] server_mac_secret = new byte[hash_size]; |
| byte[] client_key = new byte[key_size]; |
| byte[] server_key = new byte[key_size]; |
| |
| boolean is_client = !session.isServer; |
| |
| System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size); |
| System.arraycopy(key_block, hash_size, |
| server_mac_secret, 0, hash_size); |
| System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size); |
| System.arraycopy(key_block, 2*hash_size+key_size, |
| server_key, 0, key_size); |
| |
| IvParameterSpec clientIV = null; |
| IvParameterSpec serverIV = null; |
| |
| if (is_exportabe) { |
| if (logger != null) { |
| logger.println("ConnectionStateSSLv3: is_exportable"); |
| } |
| |
| MessageDigest md5 = MessageDigest.getInstance("MD5"); |
| md5.update(client_key); |
| md5.update(clientRandom); |
| md5.update(serverRandom); |
| client_key = md5.digest(); |
| |
| md5.update(server_key); |
| md5.update(serverRandom); |
| md5.update(clientRandom); |
| server_key = md5.digest(); |
| |
| key_size = cipherSuite.expandedKeyMaterial; |
| |
| if (block_size != 0) { |
| md5.update(clientRandom); |
| md5.update(serverRandom); |
| clientIV = new IvParameterSpec(md5.digest(), 0, iv_size); |
| md5.update(serverRandom); |
| md5.update(clientRandom); |
| serverIV = new IvParameterSpec(md5.digest(), 0, iv_size); |
| } |
| } else if (block_size != 0) { |
| clientIV = new IvParameterSpec(key_block, |
| 2*hash_size+2*key_size, iv_size); |
| serverIV = new IvParameterSpec(key_block, |
| 2*hash_size+2*key_size+iv_size, iv_size); |
| } |
| |
| if (logger != null) { |
| logger.println("is exportable: "+is_exportabe); |
| logger.println("master_secret"); |
| logger.print(session.master_secret); |
| logger.println("client_random"); |
| logger.print(clientRandom); |
| logger.println("server_random"); |
| logger.print(serverRandom); |
| //logger.println("key_block"); |
| //logger.print(key_block); |
| logger.println("client_mac_secret"); |
| logger.print(client_mac_secret); |
| logger.println("server_mac_secret"); |
| logger.print(server_mac_secret); |
| logger.println("client_key"); |
| logger.print(client_key, 0, key_size); |
| logger.println("server_key"); |
| logger.print(server_key, 0, key_size); |
| if (clientIV != null) { |
| logger.println("client_iv"); |
| logger.print(clientIV.getIV()); |
| logger.println("server_iv"); |
| logger.print(serverIV.getIV()); |
| } else { |
| logger.println("no IV."); |
| } |
| } |
| |
| if (algName == null) { |
| encCipher = new NullCipher(); |
| decCipher = new NullCipher(); |
| } else { |
| encCipher = Cipher.getInstance(algName); |
| decCipher = Cipher.getInstance(algName); |
| if (is_client) { // client side |
| encCipher.init(Cipher.ENCRYPT_MODE, |
| new SecretKeySpec(client_key, 0, key_size, algName), |
| clientIV); |
| decCipher.init(Cipher.DECRYPT_MODE, |
| new SecretKeySpec(server_key, 0, key_size, algName), |
| serverIV); |
| } else { // server side |
| encCipher.init(Cipher.ENCRYPT_MODE, |
| new SecretKeySpec(server_key, 0, key_size, algName), |
| serverIV); |
| decCipher.init(Cipher.DECRYPT_MODE, |
| new SecretKeySpec(client_key, 0, key_size, algName), |
| clientIV); |
| } |
| } |
| |
| messageDigest = MessageDigest.getInstance(hashName); |
| if (is_client) { // client side |
| mac_write_secret = client_mac_secret; |
| mac_read_secret = server_mac_secret; |
| } else { // server side |
| mac_write_secret = server_mac_secret; |
| mac_read_secret = client_mac_secret; |
| } |
| if (hashName.equals("MD5")) { |
| pad_1 = SSLv3Constants.MD5pad1; |
| pad_2 = SSLv3Constants.MD5pad2; |
| } else { |
| pad_1 = SSLv3Constants.SHApad1; |
| pad_2 = SSLv3Constants.SHApad2; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new AlertException(AlertProtocol.INTERNAL_ERROR, |
| new SSLProtocolException( |
| "Error during computation of security parameters")); |
| } |
| } |
| |
| /** |
| * Creates the GenericStreamCipher or GenericBlockCipher |
| * data structure for specified data of specified type. |
| * @throws AlertException if alert was occurred. |
| */ |
| @Override |
| protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) { |
| try { |
| int content_mac_length = len + hash_size; |
| int padding_length = (block_size == 0) ? 0 : getPaddingSize(++content_mac_length); |
| byte[] res = new byte[content_mac_length + padding_length]; |
| System.arraycopy(fragment, offset, res, 0, len); |
| |
| mac_material_part[0] = type; |
| mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8); |
| mac_material_part[2] = (byte) (0x0000FF & len); |
| |
| messageDigest.update(mac_write_secret); |
| messageDigest.update(pad_1); |
| messageDigest.update(write_seq_num); |
| messageDigest.update(mac_material_part); |
| messageDigest.update(fragment, offset, len); |
| byte[] digest = messageDigest.digest(); |
| messageDigest.update(mac_write_secret); |
| messageDigest.update(pad_2); |
| messageDigest.update(digest); |
| digest = messageDigest.digest(); |
| System.arraycopy(digest, 0, res, len, hash_size); |
| |
| //if (logger != null) { |
| // logger.println("MAC Material:"); |
| // logger.print(write_seq_num); |
| // logger.print(mac_material_header); |
| // logger.print(fragment, offset, len); |
| //} |
| |
| if (block_size != 0) { |
| // do padding: |
| Arrays.fill(res, content_mac_length-1, |
| res.length, (byte) (padding_length)); |
| } |
| if (logger != null) { |
| logger.println("SSLRecordProtocol.encrypt: " |
| + (block_size != 0 |
| ? "GenericBlockCipher with padding[" |
| +padding_length+"]:" |
| : "GenericStreamCipher:")); |
| logger.print(res); |
| } |
| byte[] rez = new byte[encCipher.getOutputSize(res.length)]; |
| encCipher.update(res, 0, res.length, rez); |
| incSequenceNumber(write_seq_num); |
| return rez; |
| } catch (GeneralSecurityException e) { |
| e.printStackTrace(); |
| throw new AlertException(AlertProtocol.INTERNAL_ERROR, |
| new SSLProtocolException("Error during the encryption")); |
| } |
| } |
| |
| /** |
| * Retrieves the fragment of the Plaintext structure of |
| * the specified type from the provided data. |
| * @throws AlertException if alert was occured. |
| */ |
| @Override |
| protected byte[] decrypt(byte type, byte[] fragment, |
| int offset, int len) { |
| // plain data of the Generic[Stream|Block]Cipher structure |
| byte[] data = decCipher.update(fragment, offset, len); |
| // the 'content' part of the structure |
| byte[] content; |
| if (block_size != 0) { |
| // check padding |
| int padding_length = data[data.length-1] & 0xFF; |
| for (int i=0; i<padding_length; i++) { |
| if ((data[data.length-2-i] & 0xFF) != padding_length) { |
| throw new AlertException( |
| AlertProtocol.DECRYPTION_FAILED, |
| new SSLProtocolException( |
| "Received message has bad padding")); |
| } |
| } |
| content = new byte[data.length - hash_size - padding_length - 1]; |
| } else { |
| content = new byte[data.length - hash_size]; |
| } |
| |
| byte[] mac_value; |
| |
| mac_material_part[0] = type; |
| mac_material_part[1] = (byte) ((0x00FF00 & content.length) >> 8); |
| mac_material_part[2] = (byte) (0x0000FF & content.length); |
| |
| messageDigest.update(mac_read_secret); |
| messageDigest.update(pad_1); |
| messageDigest.update(read_seq_num); |
| messageDigest.update(mac_material_part); |
| messageDigest.update(data, 0, content.length); |
| mac_value = messageDigest.digest(); |
| messageDigest.update(mac_read_secret); |
| messageDigest.update(pad_2); |
| messageDigest.update(mac_value); |
| mac_value = messageDigest.digest(); |
| |
| if (logger != null) { |
| logger.println("Decrypted:"); |
| logger.print(data); |
| //logger.println("MAC Material:"); |
| //logger.print(read_seq_num); |
| //logger.print(mac_material_header); |
| //logger.print(data, 0, content.length); |
| logger.println("Expected mac value:"); |
| logger.print(mac_value); |
| } |
| // checking the mac value |
| for (int i=0; i<hash_size; i++) { |
| if (mac_value[i] != data[i+content.length]) { |
| throw new AlertException(AlertProtocol.BAD_RECORD_MAC, |
| new SSLProtocolException("Bad record MAC")); |
| } |
| } |
| System.arraycopy(data, 0, content, 0, content.length); |
| incSequenceNumber(read_seq_num); |
| return content; |
| } |
| |
| /** |
| * Shutdown the protocol. It will be impossible to use the instance |
| * after the calling of this method. |
| */ |
| @Override |
| protected void shutdown() { |
| Arrays.fill(mac_write_secret, (byte) 0); |
| Arrays.fill(mac_read_secret, (byte) 0); |
| super.shutdown(); |
| } |
| } |
| |