//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.cms;

import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;

import com.dstc.security.provider.OID;
import com.dstc.security.x509.AlgorithmId;
import com.dstc.security.cms.v1.RecipientIdentifier;
import com.dstc.security.cms.v1.RecipientInfo;
import com.dstc.security.cms.v1.IssuerAndSerialNumber;

/**
 * <p>A class representing a recipient of a CMS enveloped message.
 *
 * <p>Only the constructor which takes an X509Certificate of the
 * recipient is publicly accessible. Recipient objects are passed
 * into an EnvelopedMessage object by invoking addRecipient()
 *
 * @version 0.99, 99/01/26
 * @author Ming Yung
 *
 */
public class Recipient
{
  private String keyEncryptionAlg;
  private PublicKey keyEncryptingKey;
  private IssuerAndSerialNumber issuerAndSerialNumber;
  private byte[] encKey;

  /**
   * Constructs a Recipient from its user certificate
   */
  public Recipient(X509Certificate cert)
  {
    this.keyEncryptingKey = cert.getPublicKey();
    this.issuerAndSerialNumber = new IssuerAndSerialNumber(cert);
    this.keyEncryptionAlg = this.keyEncryptingKey.getAlgorithm();
  }

  /**
   * Constructs a Recipient from a RecipientInfo
   *
   * (package scope: used only by EnvelopedMessage)
   */
  Recipient(RecipientInfo info)
  {
    this.issuerAndSerialNumber = info.getRecipientIdentifier();
    this.encKey = info.getEncryptedKey();
    this.keyEncryptionAlg = "RSA";
  }

  /**
   * Returns the key encryption algorithm this Recipient uses
   *
   * (package scope: used only by EnvelopedMessage)
   */
  String getKeyEncryptionAlgorithm()
  {
    return this.keyEncryptionAlg;
  }

  /**
   * Returns the IssuerAndSerialNumber for this Recipient
   *
   * (package scope: used only by EnvelopedMessage)
   */
  IssuerAndSerialNumber getIssuerAndSerialNumber()
  {
    return this.issuerAndSerialNumber;
  }

  /**
   * Encrypts the given session key bytes for this Recipient and returns
   * a RecipientInfo 
   *
   * (package scope: used only by EnvelopedMessage)
   */
  RecipientInfo encryptKey(byte[] keyBytes) throws CMSException
  {
    this.encKey = this.encrypt(keyBytes);
    return new RecipientInfo(this.issuerAndSerialNumber, 
      new AlgorithmId(OID.getAlgOid(this.keyEncryptionAlg)), this.encKey);
  }

  /**
   * Decrypts and returns the session key in the RecipientInfo with a given
   * PrivateKey
   *
   * (package scope: used only by EnvelopedMessage)
   */
  byte[] decryptKey(PrivateKey priv) throws CMSException
  {
    try
    {
      Cipher cipher = Cipher.getInstance(this.keyEncryptionAlg);
      cipher.init(Cipher.DECRYPT_MODE, priv, null, null);
      return cipher.doFinal(this.encKey);
    }
    catch (Exception e)
    {
      throw new CMSException(e.toString());
    }
  }

  /**
   * Encrypts and returns the supplied key bytes
   */
  private byte[] encrypt(byte[] keyBytes) throws CMSException
  {
    try
    {
      Cipher cipher = Cipher.getInstance(this.keyEncryptionAlg);
      cipher.init(Cipher.ENCRYPT_MODE, this.keyEncryptingKey, null, null);
      return cipher.doFinal(keyBytes);
    }
    catch (Exception e)
    {
      throw new CMSException(e.toString());
    }
  }
}
