/**
  * Encrypts the content passed-in.
  *
  * @param key the key used for the underlying cypher.
  * @param content the content to encrypted
  * @return {@code String} the encoded content as base64 encoded string.
  * @throws Exception
  */
 public static String encrypt(final String key, final String content) throws Exception {
   final Cipher cipher = Cipher.getInstance(TRANSOFRMATION);
   final IvParameterSpec iv = getIV();
   cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec(key), iv);
   final byte[] encrypted = cipher.doFinal(content.getBytes(ASCII));
   final String base64 = Base64.encodeBase64URLSafeString(prependIV(encrypted, iv.getIV()));
   return URLEncoder.encode(base64, ASCII.displayName());
 }
Пример #2
0
 // see JCE spec
 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)
     throws InvalidKeyException, InvalidAlgorithmParameterException {
   byte[] ivValue;
   if (params != null) {
     if (params instanceof IvParameterSpec == false) {
       throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
     }
     IvParameterSpec ivSpec = (IvParameterSpec) params;
     ivValue = ivSpec.getIV();
   } else {
     ivValue = null;
   }
   implInit(opmode, key, ivValue, random);
 }
Пример #3
0
 // see JCE spec
 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
     throws InvalidKeyException, InvalidAlgorithmParameterException {
   byte[] ivValue;
   if (params != null) {
     try {
       IvParameterSpec ivSpec = (IvParameterSpec) params.getParameterSpec(IvParameterSpec.class);
       ivValue = ivSpec.getIV();
     } catch (InvalidParameterSpecException e) {
       throw new InvalidAlgorithmParameterException("Could not decode IV", e);
     }
   } else {
     ivValue = null;
   }
   implInit(opmode, key, ivValue, random);
 }
Пример #4
0
 public byte[] encode() {
   byte iv[] = ivParameter == null ? new byte[0] : ivParameter.getIV();
   byte[] tab = new byte[4];
   Bits.putInt(tab, 0, type.ordinal());
   return Bits.concateEncodingWithShortSizedTabs(
       tab,
       Bits.concateEncodingWithShortSizedTabs(
           iv, SymmetricEncryptionType.encodeSecretKey(secretKey)));
 }
Пример #5
0
  protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)
      throws InvalidKeyException, InvalidAlgorithmParameterException {
    CipherParameters param;

    if (key instanceof BCPBEKey) {
      BCPBEKey k = (BCPBEKey) key;

      if (params instanceof PBEParameterSpec) {
        param = PBE.Util.makePBEParameters(k, params, wrapEngine.getAlgorithmName());
      } else if (k.getParam() != null) {
        param = k.getParam();
      } else {
        throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
      }
    } else {
      param = new KeyParameter(key.getEncoded());
    }

    if (params instanceof IvParameterSpec) {
      IvParameterSpec iv = (IvParameterSpec) params;
      param = new ParametersWithIV(param, iv.getIV());
    }

    if (param instanceof KeyParameter && ivSize != 0) {
      iv = new byte[ivSize];
      random.nextBytes(iv);
      param = new ParametersWithIV(param, iv);
    }

    switch (opmode) {
      case Cipher.WRAP_MODE:
        wrapEngine.init(true, param);
        break;
      case Cipher.UNWRAP_MODE:
        wrapEngine.init(false, param);
        break;
      case Cipher.ENCRYPT_MODE:
      case Cipher.DECRYPT_MODE:
        throw new IllegalArgumentException("engine only valid for wrapping");
      default:
        System.out.println("eeek!");
    }
  }
Пример #6
0
 private String decrypt(String cipherText, String key, int nBits) throws Exception {
   if (!(nBits == 128 || nBits == 192 || nBits == 256)) {
     return "Error: Must be a key mode of either 128, 192, 256 bits";
   }
   if (cipherText == null || key == null) {
     return "Error: cipher and/or key equals null";
   }
   byte[] decrypted;
   nBits = nBits / 8;
   byte[] data = Base64.decode(cipherText.toCharArray());
   /*
    * CHECK: we should always use getBytes("UTF-8") or with wanted charset, never system charset!
    */
   byte[] k = Arrays.copyOf(key.getBytes(), nBits);
   /* AES/CTR/NoPadding (SIC == CTR) */
   org.bouncycastle.crypto.BufferedBlockCipher cipher =
       new org.bouncycastle.crypto.BufferedBlockCipher(
           new org.bouncycastle.crypto.modes.SICBlockCipher(
               new org.bouncycastle.crypto.engines.AESEngine()));
   cipher.reset();
   SecretKey secretKey = generateSecretKey(k, nBits);
   byte[] nonceBytes = Arrays.copyOf(Arrays.copyOf(data, 8), nBits / 2);
   IvParameterSpec nonce = new IvParameterSpec(nonceBytes);
   /* true == encrypt; false == decrypt */
   cipher.init(
       true,
       new org.bouncycastle.crypto.params.ParametersWithIV(
           new org.bouncycastle.crypto.params.KeyParameter(secretKey.getEncoded()),
           nonce.getIV()));
   decrypted = new byte[cipher.getOutputSize(data.length - 8)];
   int decLength = cipher.processBytes(data, 8, data.length - 8, decrypted, 0);
   cipher.doFinal(decrypted, decLength);
   /*
    * CHECK: we should always use new String (bytes,charset) to avoid issues with system charset and utf-8
    */
   return new String(decrypted);
 }
Пример #7
0
 @Override
 public boolean equals(Object o) {
   if (o == null) return false;
   if (o == this) return true;
   if (o instanceof SymmetricSecretKey) {
     SymmetricSecretKey other = ((SymmetricSecretKey) o);
     return secretKey.equals(other.secretKey)
         && type == other.type
         && ((ivParameter == null && other.ivParameter == null)
             || (ivParameter != null
                 && other.ivParameter != null
                 && Arrays.equals(ivParameter.getIV(), other.ivParameter.getIV())));
   }
   return false;
 }
Пример #8
0
  private static IvParameterSpec calculateIVForOffset(
      final IvParameterSpec iv, final long blockOffset) {
    final BigInteger ivBI = new BigInteger(1, iv.getIV());
    final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset / BLOCK_SIZE));

    final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
    final IvParameterSpec ivForOffset;
    if (ivForOffsetBA.length >= BLOCK_SIZE) {
      ivForOffset =
          new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - BLOCK_SIZE, BLOCK_SIZE);
    } else {
      final byte[] ivForOffsetBASized = new byte[BLOCK_SIZE];
      System.arraycopy(
          ivForOffsetBA,
          0,
          ivForOffsetBASized,
          BLOCK_SIZE - ivForOffsetBA.length,
          ivForOffsetBA.length);
      ivForOffset = new IvParameterSpec(ivForOffsetBASized);
    }

    return ivForOffset;
  }
Пример #9
0
 /**
  * Takes correctly padded data and encrypts it
  *
  * @param data correctly padded data
  * @return
  * @throws CryptoException
  */
 public byte[] encrypt(byte[] data) throws CryptoException {
   try {
     byte[] ciphertext;
     if (useExplicitIv) {
       ciphertext = ArrayConverter.concatenate(encryptIv.getIV(), encryptCipher.doFinal(data));
     } else {
       encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey, encryptIv);
       ciphertext = encryptCipher.doFinal(data);
       encryptIv =
           new IvParameterSpec(
               Arrays.copyOfRange(
                   ciphertext,
                   ciphertext.length - decryptCipher.getBlockSize(),
                   ciphertext.length));
     }
     return ciphertext;
   } catch (BadPaddingException
       | IllegalBlockSizeException
       | InvalidAlgorithmParameterException
       | InvalidKeyException ex) {
     throw new CryptoException(ex);
   }
 }
  /**
   * Creates the instance of TLS v1 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 TLS v 1.0
   *     Protocol specification. (http://www.ietf.org/rfc/rfc2246.txt)
   */
  protected ConnectionStateTLS(SSLSessionImpl session) {
    try {
      CipherSuite cipherSuite = session.cipherSuite;

      hash_size = cipherSuite.getMACLength();
      boolean is_exportabe = cipherSuite.isExportable();
      int key_size = (is_exportabe) ? cipherSuite.keyMaterial : cipherSuite.expandedKeyMaterial;
      int iv_size = cipherSuite.getBlockSize();

      String algName = cipherSuite.getBulkEncryptionAlgorithm();
      String macName = cipherSuite.getHmacName();
      if (logger != null) {
        logger.println("ConnectionStateTLS.create:");
        logger.println("  cipher suite name: " + cipherSuite.getName());
        logger.println("  encryption alg name: " + algName);
        logger.println("  mac alg name: " + macName);
        logger.println("  hash size: " + hash_size);
        logger.println("  block size: " + iv_size);
        logger.println("  IV size (== block 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(key_block, session.master_secret, KEY_EXPANSION_LABEL, 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;

      is_block_cipher = (iv_size > 0);
      // do not count, as block_size is always 8
      // block_size = iv_size;

      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) {
        System.arraycopy(clientRandom, 0, seed, 0, clientRandom.length);
        System.arraycopy(serverRandom, 0, seed, clientRandom.length, serverRandom.length);
        byte[] final_client_key = new byte[cipherSuite.expandedKeyMaterial];
        byte[] final_server_key = new byte[cipherSuite.expandedKeyMaterial];
        PRF.computePRF(final_client_key, client_key, CLIENT_WRITE_KEY_LABEL, seed);
        PRF.computePRF(final_server_key, server_key, SERVER_WRITE_KEY_LABEL, seed);
        client_key = final_client_key;
        server_key = final_server_key;
        if (is_block_cipher) {
          byte[] iv_block = new byte[2 * iv_size];
          PRF.computePRF(iv_block, null, IV_BLOCK_LABEL, seed);
          clientIV = new IvParameterSpec(iv_block, 0, iv_size);
          serverIV = new IvParameterSpec(iv_block, iv_size, iv_size);
        }
      } else if (is_block_cipher) {
        clientIV = new IvParameterSpec(key_block, 2 * (hash_size + key_size), iv_size);
        serverIV = new IvParameterSpec(key_block, 2 * (hash_size + 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);
        logger.println("server_key");
        logger.print(server_key);
        if (clientIV == null) {
          logger.println("no IV.");
        } else {
          logger.println("client_iv");
          logger.print(clientIV.getIV());
          logger.println("server_iv");
          logger.print(serverIV.getIV());
        }
      }

      encCipher = Cipher.getInstance(algName);
      decCipher = Cipher.getInstance(algName);
      encMac = Mac.getInstance(macName);
      decMac = Mac.getInstance(macName);

      if (is_client) { // client side
        encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(client_key, algName), clientIV);
        decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(server_key, algName), serverIV);
        encMac.init(new SecretKeySpec(client_mac_secret, macName));
        decMac.init(new SecretKeySpec(server_mac_secret, macName));
      } else { // server side
        encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(server_key, algName), serverIV);
        decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(client_key, algName), clientIV);
        encMac.init(new SecretKeySpec(server_mac_secret, macName));
        decMac.init(new SecretKeySpec(client_mac_secret, macName));
      }
    } catch (Exception e) {
      e.printStackTrace();
      throw new AlertException(
          AlertProtocol.INTERNAL_ERROR,
          new SSLProtocolException("Error during computation of security parameters"));
    }
  }
Пример #11
0
  protected void saveToJarXML(HashMap detailsToJarXML)
      throws TransformerException, ParserConfigurationException {
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
    Document document = documentBuilder.newDocument();

    Element rootElement = document.createElement("JarConfig");
    document.appendChild(rootElement);

    if (detailsToJarXML.get(FileSecConstants.JARXML_NOTBEFORE) != null) {
      Element notBeforeElement = document.createElement(FileSecConstants.JARXML_NOTBEFORE);
      String notBefore = (String) detailsToJarXML.get(FileSecConstants.JARXML_NOTBEFORE);
      notBeforeElement.appendChild(document.createTextNode(notBefore));
      rootElement.appendChild(notBeforeElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_NOTAFTER) != null) {
      Element notAfterElement = document.createElement(FileSecConstants.JARXML_NOTAFTER);
      String notAfter = (String) detailsToJarXML.get(FileSecConstants.JARXML_NOTAFTER);
      notAfterElement.appendChild(document.createTextNode(notAfter));
      rootElement.appendChild(notAfterElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_CIPHER) != null) {
      Element symmetricCipherElement =
          document.createElement(FileSecConstants.JARXML_SYMMETRIC_CIPHER);
      String symmetricCipher =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_CIPHER);
      symmetricCipherElement.appendChild(document.createTextNode(symmetricCipher));
      rootElement.appendChild(symmetricCipherElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_KEYTYPE) != null) {
      Element symmetricKeyTypeElement =
          document.createElement(FileSecConstants.JARXML_SYMMETRIC_KEYTYPE);
      String symmetricKeyType =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_KEYTYPE);
      symmetricKeyTypeElement.appendChild(document.createTextNode(symmetricKeyType));
      rootElement.appendChild(symmetricKeyTypeElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_KEYSIZE) != null) {
      Element symmetricKeySizeElement =
          document.createElement(FileSecConstants.JARXML_SYMMETRIC_KEYSIZE);
      String symmetricKeySize =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_SYMMETRIC_KEYSIZE);
      symmetricKeySizeElement.appendChild(document.createTextNode(symmetricKeySize));
      rootElement.appendChild(symmetricKeySizeElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_ASYMMETRIC_CIPHER) != null) {
      Element asymmetricCipherElement =
          document.createElement(FileSecConstants.JARXML_ASYMMETRIC_CIPHER);
      String asymmetricCipher =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_ASYMMETRIC_CIPHER);
      asymmetricCipherElement.appendChild(document.createTextNode(asymmetricCipher));
      rootElement.appendChild(asymmetricCipherElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_ASYMMETRIC_SIGN) != null) {
      Element asymmetricCipherElement =
          document.createElement(FileSecConstants.JARXML_ASYMMETRIC_SIGN);
      String asymmetricCipher =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_ASYMMETRIC_SIGN);
      asymmetricCipherElement.appendChild(document.createTextNode(asymmetricCipher));
      rootElement.appendChild(asymmetricCipherElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_PBE_KEYTYPE) != null) {
      Element pbeCiperKeyTypeElement = document.createElement(FileSecConstants.JARXML_PBE_KEYTYPE);
      String pbeCiperKeyType = (String) detailsToJarXML.get(FileSecConstants.JARXML_PBE_KEYTYPE);
      pbeCiperKeyTypeElement.appendChild(document.createTextNode(pbeCiperKeyType));
      rootElement.appendChild(pbeCiperKeyTypeElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_PBE_CIPHER) != null) {
      Element pbeCipherElement = document.createElement(FileSecConstants.JARXML_PBE_CIPHER);
      String pbeCipher = (String) detailsToJarXML.get(FileSecConstants.JARXML_PBE_CIPHER);
      pbeCipherElement.appendChild(document.createTextNode(pbeCipher));
      rootElement.appendChild(pbeCipherElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_MAC_HASHALGORITHM) != null) {
      Element MACHashAlgorithmElement =
          document.createElement(FileSecConstants.JARXML_MAC_HASHALGORITHM);
      String strMACHashAlgorithm =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_MAC_HASHALGORITHM);
      MACHashAlgorithmElement.appendChild(document.createTextNode(strMACHashAlgorithm));
      rootElement.appendChild(MACHashAlgorithmElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_SIGNATURE) != null) {
      Element sigElement = document.createElement(FileSecConstants.JARXML_SIGNATURE);
      String signature = (String) detailsToJarXML.get(FileSecConstants.JARXML_SIGNATURE);
      sigElement.appendChild(document.createTextNode(signature));
      rootElement.appendChild(sigElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_SIGNING_ALIAS) != null) {
      Element aliasElement = document.createElement(FileSecConstants.JARXML_SIGNING_ALIAS);
      String alias = (String) detailsToJarXML.get(FileSecConstants.JARXML_SIGNING_ALIAS);
      aliasElement.appendChild(document.createTextNode(alias));
      rootElement.appendChild(aliasElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_ENCRYPTED_PKI_SESSIONKEY) != null) {
      Element sessionKeyElement =
          document.createElement(FileSecConstants.JARXML_ENCRYPTED_PKI_SESSIONKEY);
      String encSessionKey =
          (String) detailsToJarXML.get(FileSecConstants.JARXML_ENCRYPTED_PKI_SESSIONKEY);
      sessionKeyElement.appendChild(document.createTextNode(encSessionKey));
      rootElement.appendChild(sessionKeyElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_IVPARAM) != null) {
      Element ivParamElement = document.createElement(FileSecConstants.JARXML_IVPARAM);
      IvParameterSpec ivParam =
          (IvParameterSpec) detailsToJarXML.get(FileSecConstants.JARXML_IVPARAM);
      BASE64Encoder encoder = new BASE64Encoder();
      String strIvParam = encoder.encode(ivParam.getIV());
      ivParamElement.appendChild(document.createTextNode(strIvParam));
      rootElement.appendChild(ivParamElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_MAC) != null) {
      Element macElement = document.createElement(FileSecConstants.JARXML_MAC);
      String mac = (String) detailsToJarXML.get(FileSecConstants.JARXML_MAC);
      macElement.appendChild(document.createTextNode(mac));
      rootElement.appendChild(macElement);
    }

    if (detailsToJarXML.get(FileSecConstants.JARXML_ENCRYPTED_SESSIONKEY_MAP) != null) {
      Element encryptedSessionKeyMapElement =
          document.createElement(FileSecConstants.JARXML_ENCRYPTED_PBE_SESSIONKEY);
      ArrayList encryptedSessionKeyMap =
          (ArrayList) detailsToJarXML.get(FileSecConstants.JARXML_ENCRYPTED_SESSIONKEY_MAP);
      ArrayList hashKeyMap =
          (ArrayList) detailsToJarXML.get(FileSecConstants.JARXML_HASHED_SESSIONKEY_MAP);
      for (int iKeyCount = 0; iKeyCount < encryptedSessionKeyMap.size(); iKeyCount++) {
        String encryptedKey = (String) encryptedSessionKeyMap.get(iKeyCount);
        String hashOfKey = (String) hashKeyMap.get(iKeyCount);
        Element keyElement = document.createElement(FileSecConstants.JARXML_KEY);
        keyElement.appendChild(document.createTextNode(encryptedKey));
        Element hashElement = document.createElement(FileSecConstants.JARXML_HASH);
        hashElement.appendChild(document.createTextNode(hashOfKey));
        encryptedSessionKeyMapElement.appendChild(keyElement);
        encryptedSessionKeyMapElement.appendChild(hashElement);
      }
      rootElement.appendChild(encryptedSessionKeyMapElement);
    }
    SimpleDateFormat formatter = new SimpleDateFormat(FileSecConstants.DATE_FORMAT);
    Date currentTimeStamp = new Date();
    String strCurrentTimeStamp = formatter.format(currentTimeStamp);

    Element currentTimeElement = document.createElement(FileSecConstants.JARXML_CURRENT_TIMESTAMP);
    currentTimeElement.appendChild(document.createTextNode(strCurrentTimeStamp));
    rootElement.appendChild(currentTimeElement);

    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    DOMSource source = new DOMSource(document);
    StreamResult result = new StreamResult(new File(FileSecConstants.DEFAULT_JAR_CONFIG_FILE));
    transformer.transform(source, result);
  }
Пример #12
-1
  /*
   * Calculate the keys needed for this connection, once the session's
   * master secret has been calculated.  Uses the master key and nonces;
   * the amount of keying material generated is a function of the cipher
   * suite that's been negotiated.
   *
   * This gets called both on the "full handshake" (where we exchanged
   * a premaster secret and started a new session) as well as on the
   * "fast handshake" (where we just resumed a pre-existing session).
   */
  void calculateConnectionKeys(SecretKey masterKey) {
    /*
     * For both the read and write sides of the protocol, we use the
     * master to generate MAC secrets and cipher keying material.  Block
     * ciphers need initialization vectors, which we also generate.
     *
     * First we figure out how much keying material is needed.
     */
    int hashSize = cipherSuite.macAlg.size;
    boolean is_exportable = cipherSuite.exportable;
    BulkCipher cipher = cipherSuite.cipher;
    int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;

    // Which algs/params do we need to use?
    String keyMaterialAlg;
    PRF prf;

    if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
      keyMaterialAlg = "SunTls12KeyMaterial";
      prf = cipherSuite.prfAlg;
    } else {
      keyMaterialAlg = "SunTlsKeyMaterial";
      prf = P_NONE;
    }

    String prfHashAlg = prf.getPRFHashAlg();
    int prfHashLength = prf.getPRFHashLength();
    int prfBlockSize = prf.getPRFBlockSize();

    TlsKeyMaterialParameterSpec spec =
        new TlsKeyMaterialParameterSpec(
            masterKey,
            protocolVersion.major,
            protocolVersion.minor,
            clnt_random.random_bytes,
            svr_random.random_bytes,
            cipher.algorithm,
            cipher.keySize,
            expandedKeySize,
            cipher.ivSize,
            hashSize,
            prfHashAlg,
            prfHashLength,
            prfBlockSize);

    try {
      KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
      kg.init(spec);
      TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey();

      clntWriteKey = keySpec.getClientCipherKey();
      svrWriteKey = keySpec.getServerCipherKey();

      // Return null if IVs are not supposed to be generated.
      // e.g. TLS 1.1+.
      clntWriteIV = keySpec.getClientIv();
      svrWriteIV = keySpec.getServerIv();

      clntMacSecret = keySpec.getClientMacKey();
      svrMacSecret = keySpec.getServerMacKey();
    } catch (GeneralSecurityException e) {
      throw new ProviderException(e);
    }

    //
    // Dump the connection keys as they're generated.
    //
    if (debug != null && Debug.isOn("keygen")) {
      synchronized (System.out) {
        HexDumpEncoder dump = new HexDumpEncoder();

        System.out.println("CONNECTION KEYGEN:");

        // Inputs:
        System.out.println("Client Nonce:");
        printHex(dump, clnt_random.random_bytes);
        System.out.println("Server Nonce:");
        printHex(dump, svr_random.random_bytes);
        System.out.println("Master Secret:");
        printHex(dump, masterKey.getEncoded());

        // Outputs:
        System.out.println("Client MAC write Secret:");
        printHex(dump, clntMacSecret.getEncoded());
        System.out.println("Server MAC write Secret:");
        printHex(dump, svrMacSecret.getEncoded());

        if (clntWriteKey != null) {
          System.out.println("Client write key:");
          printHex(dump, clntWriteKey.getEncoded());
          System.out.println("Server write key:");
          printHex(dump, svrWriteKey.getEncoded());
        } else {
          System.out.println("... no encryption keys used");
        }

        if (clntWriteIV != null) {
          System.out.println("Client write IV:");
          printHex(dump, clntWriteIV.getIV());
          System.out.println("Server write IV:");
          printHex(dump, svrWriteIV.getIV());
        } else {
          if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
            System.out.println("... no IV derived for this protocol");
          } else {
            System.out.println("... no IV used for this cipher");
          }
        }
        System.out.flush();
      }
    }
  }