public ASN1Sequence generateRecipientEncryptedKeys(
      AlgorithmIdentifier keyAgreeAlgorithm,
      AlgorithmIdentifier keyEncryptionAlgorithm,
      GenericKey contentEncryptionKey)
      throws CMSException {
    init(keyAgreeAlgorithm.getAlgorithm());

    PrivateKey senderPrivateKey = this.senderPrivateKey;

    ASN1ObjectIdentifier keyAgreementOID = keyAgreeAlgorithm.getAlgorithm();

    if (keyAgreementOID.getId().equals(CMSEnvelopedGenerator.ECMQV_SHA1KDF)) {
      senderPrivateKey =
          new MQVPrivateKeySpec(
              senderPrivateKey, ephemeralKP.getPrivate(), ephemeralKP.getPublic());
    }

    ASN1EncodableVector recipientEncryptedKeys = new ASN1EncodableVector();
    for (int i = 0; i != recipientIDs.size(); i++) {
      PublicKey recipientPublicKey = (PublicKey) recipientKeys.get(i);
      KeyAgreeRecipientIdentifier karId = (KeyAgreeRecipientIdentifier) recipientIDs.get(i);

      if (keyAgreementOID.getId().equals(CMSEnvelopedGenerator.ECMQV_SHA1KDF)) {
        recipientPublicKey = new MQVPublicKeySpec(recipientPublicKey, recipientPublicKey);
      }

      try {
        // Use key agreement to choose a wrap key for this recipient
        KeyAgreement keyAgreement = helper.createKeyAgreement(keyAgreementOID);
        keyAgreement.init(senderPrivateKey, random);
        keyAgreement.doPhase(recipientPublicKey, true);
        SecretKey keyEncryptionKey =
            keyAgreement.generateSecret(keyEncryptionAlgorithm.getAlgorithm().getId());

        // Wrap the content encryption key with the agreement key
        Cipher keyEncryptionCipher = helper.createCipher(keyEncryptionAlgorithm.getAlgorithm());

        keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);

        byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));

        ASN1OctetString encryptedKey = new DEROctetString(encryptedKeyBytes);

        recipientEncryptedKeys.add(new RecipientEncryptedKey(karId, encryptedKey));
      } catch (GeneralSecurityException e) {
        throw new CMSException("cannot perform agreement step: " + e.getMessage(), e);
      }
    }

    return new DERSequence(recipientEncryptedKeys);
  }
  protected Key extractSecretKey(
      AlgorithmIdentifier keyEncryptionAlgorithm,
      AlgorithmIdentifier encryptedKeyAlgorithm,
      byte[] encryptedEncryptionKey)
      throws CMSException {
    AsymmetricKeyUnwrapper unwrapper =
        helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey);

    try {
      return CMSUtils.getJceKey(
          unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
    } catch (OperatorException e) {
      throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
    }
  }
  private void init(ASN1ObjectIdentifier keyAgreementOID) throws CMSException {
    if (random == null) {
      random = new SecureRandom();
    }

    if (keyAgreementOID.equals(CMSAlgorithm.ECMQV_SHA1KDF)) {
      if (ephemeralKP == null) {
        try {
          ECParameterSpec ecParamSpec = ((ECPublicKey) senderPublicKey).getParams();

          KeyPairGenerator ephemKPG = helper.createKeyPairGenerator(keyAgreementOID);

          ephemKPG.initialize(ecParamSpec, random);

          ephemeralKP = ephemKPG.generateKeyPair();
        } catch (InvalidAlgorithmParameterException e) {
          throw new CMSException(
              "cannot determine MQV ephemeral key pair parameters from public key: " + e);
        }
      }
    }
  }