Package codec.pkcs7

Source Code of codec.pkcs7.EncryptedContentInfo

/* ========================================================================
*
*  This file is part of CODEC, which is a Java package for encoding
*  and decoding ASN.1 data structures.
*
*  Author: Fraunhofer Institute for Computer Graphics Research IGD
*          Department A8: Security Technology
*          Fraunhoferstr. 5, 64283 Darmstadt, Germany
*
*  Rights: Copyright (c) 2004 by Fraunhofer-Gesellschaft
*          zur Foerderung der angewandten Forschung e.V.
*          Hansastr. 27c, 80686 Munich, Germany.
*
* ------------------------------------------------------------------------
*
*  The software package is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License as
*  published by the Free Software Foundation; either version 2.1 of the
*  License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this software package; if not, write to the Free
*  Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
*  MA 02110-1301, USA or obtain a copy of the license at
*  http://www.fsf.org/licensing/licenses/lgpl.txt.
*
* ------------------------------------------------------------------------
*
*  The CODEC library can solely be used and distributed according to
*  the terms and conditions of the GNU Lesser General Public License .
*
*  The CODEC library has not been tested for the use or application
*  for a determined purpose. It is a developing version that can
*  possibly contain errors. Therefore, Fraunhofer-Gesellschaft zur
*  Foerderung der angewandten Forschung e.V. does not warrant that the
*  operation of the CODEC library will be uninterrupted or error-free.
*  Neither does Fraunhofer-Gesellschaft zur Foerderung der angewandten
*  Forschung e.V. warrant that the CODEC library will operate and
*  interact in an uninterrupted or error-free way together with the
*  computer program libraries of third parties which the CODEC library
*  accesses and which are distributed together with the CODEC library.
*
*  Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
*  does not warrant that the operation of the third parties's computer
*  program libraries themselves which the CODEC library accesses will
*  be uninterrupted or error-free.
*
*  Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
*  shall not be liable for any errors or direct, indirect, special,
*  incidental or consequential damages, including lost profits resulting
*  from the combination of the CODEC library with software of any user
*  or of any third party or resulting from the implementation of the
*  CODEC library in any products, systems or services of any user or
*  of any third party.
*
*  Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
*  does not provide any warranty nor any liability that utilization of
*  the CODEC library will not interfere with third party intellectual
*  property rights or with any other protected third party rights or will
*  cause damage to third parties. Fraunhofer Gesellschaft zur Foerderung
*  der angewandten Forschung e.V. is currently not aware of any such
*  rights.
*
*  The CODEC library is supplied without any accompanying services.
*
* ========================================================================
*/
package codec.pkcs7;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.NoSuchElementException;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;

import codec.InconsistentStateException;
import codec.asn1.ASN1Exception;
import codec.asn1.ASN1ObjectIdentifier;
import codec.asn1.ASN1OctetString;
import codec.asn1.ASN1Sequence;
import codec.asn1.ASN1TaggedType;
import codec.asn1.Decoder;
import codec.x501.BadNameException;
import codec.x509.AlgorithmIdentifier;

/**
* This class represents a <code>EncryptedContentInfo</code> as defined in <a
* href="http://www.rsa.com/rsalabs/pubs/PKCS/html/pkcs-7.html"> PKCS#7</a>.
* The ASN.1 definition of this structure is
* <p>
*
* <pre>
* EncryptedContentInfo ::= SEQUENCE {
*   contentType ContentType,
*   contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
*   encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
* }
* EncryptedContent ::= OCTET STRING
* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
* </pre>
*
* <p>
* <code>contentType</code> indicates the type of content embedded in the
* EncryptedContent. The encryptedContent is optional; if it is not included in
* this structure then it must be provided by other means (such as a detached
* file).
* <p>
*
* PKCS#7 specifies six content types, of which five are supported:
* {@link Data data}, {@link SignedData signedData},
* {@link EnvelopedData envelopedData},
* {@link SignedAndEnvelopedData signedAndEnvelopedData}, and
* {@link EncryptedData encryptedData}. All of these content types have
* registered OIDs.
* <p>
*
* @author Volker Roth
* @author Markus Tak
* @version "$Id: EncryptedContentInfo.java,v 1.9 2007/08/30 08:45:05 pebinger
*          Exp $"
*/
public class EncryptedContentInfo extends ASN1Sequence {
    /**
     * The size of the buffer allocated for encrypting.
     */
    public static final int BUFFER_SIZE = 4096;

    /**
     * The OID of PKCS#7 Data
     */
    private static final int[] DATA_OID = { 1, 2, 840, 113549, 1, 7, 1 };

    /**
     * The OID defining the contents of this structure.
     */
    protected ASN1ObjectIdentifier contentType_;

    /**
     * The ContentEncryptionAlgorithmIdentifier
     */
    protected AlgorithmIdentifier cAlg_;

    /**
     * The encrypted content, if present in this structure.
     */
    protected ASN1TaggedType econtent_;

    /**
     * The bulk encryption algorithm.
     */
    protected String bea_;

    /**
     * The bulk encryption key.
     */
    private SecretKey bek_;

    /**
     * The bulk encryption algorithm parameters.
     */
    private AlgorithmParameters params_;

    /**
     * Creates an instance ready for parsing. After decoding of this instance,
     * it must be initialized with one of the <code>init</code> methods,
     * before encryption or decryption operation can commence.
     */
    public EncryptedContentInfo() {
  super(3);

  contentType_ = new ASN1ObjectIdentifier();
  cAlg_ = new AlgorithmIdentifier();
  econtent_ = new ASN1TaggedType(0, new ASN1OctetString(), false, true);

  add(contentType_);
  add(cAlg_);
  add(econtent_);
    }

    /**
     * Initializes an instance with the given secret key, algorithm, and
     * parameters. The content type is set to {@link Data Data}. Instances
     * created with this constructor are ready for encryption and decryption
     * operations by means of the <code>crypt</code> methods.
     *
     * @param bea
     *                The bulk encryption algorithm name.
     * @param bek
     *                The secret bulk encryption key.
     * @param params
     *                The bulk encryption algorithm parameters.
     */
    public EncryptedContentInfo(String bea, SecretKey bek,
      AlgorithmParameters params)
      throws InvalidAlgorithmParameterException {
  if (bea == null || bek == null) {
      throw new NullPointerException("BEK or BEA is null!");
  }
  contentType_ = new ASN1ObjectIdentifier(DATA_OID);
  cAlg_ = new AlgorithmIdentifier(bea, params);
  econtent_ = new ASN1TaggedType(0, new ASN1OctetString(), false, true);

  add(contentType_);
  add(cAlg_);
  add(econtent_);

  bea_ = bea;
  bek_ = bek;
  params_ = params;
    }

    /**
     * Returns the <code>contentType</code> of this structure. This value is
     * defined only if the structure has been decoded successfully, or the
     * content has been set previously.
     *
     * @return The OID describing the <code>contentType</code> of this
     *         structure.
     */
    public ASN1ObjectIdentifier getContentType() {
  return contentType_;
    }

    /**
     * This method returns the actual <code>content</code> of this structure.
     *
     * @return The <code>content</code> or <code>null</code> if no content
     *         is available.
     */
    public byte[] getEncryptedContent() {
  if (econtent_.isOptional()) {
      return null;
  }
  ASN1OctetString v;

  v = (ASN1OctetString) econtent_.getInnerType();
  return v.getByteArray();
    }

    /**
     * Returns the name of the bulk encryption algorithm name.
     *
     * @return The algorithm name.
     * @throws IllegalStateException
     *                 if this instance is not yet initialized.
     */
    public String getAlgorithm() {
  if (bea_ == null) {
      throw new IllegalStateException(
        "Not initialized or algorithm unresolvable!");
  }
  return bea_;
    }

    /**
     * Returns the algorithm parameters of the bulk encryption algorithm
     * identifier.
     *
     * @return The algorithm parameters.
     */
    public AlgorithmParameters getParameters() {
  if (params_ == null) {
      throw new IllegalStateException(
        "Not initialized or parameters unresolvable!");
  }
  return params_;
    }

    /**
     * Returns the secret bulk encryption key.
     *
     * @return The BEK or <code>null</code>.
     * @throws IllegalStateException
     *                 if this instance is not yet initialized.
     */
    public SecretKey getSecretKey() {
  if (bek_ == null) {
      throw new IllegalStateException("Not initialized!");
  }
  return bek_;
    }

    /**
     * Initializes this instance for encryption/decryption with the BEK that is
     * stored in the given {@link RecipientInfo RecipientInfo}. The BEK is
     * decrypted with the given private key and initialized according to the
     * algorithm specified in this instance's
     * contentEncryptionAlgorithmIdentifier.
     *
     * @param kdk
     *                The private <i>Key Decryption Key</i> required to decrypt
     *                the BEK.
     * @param info
     *                The {@link RecipientInfo RecipientInfo} that holds the
     *                BEK.
     * @throws GeneralSecurityException
     *                 if some cipher related exception is thrown by the
     *                 underlying engines.
     */
    public void init(PrivateKey kdk, RecipientInfo info)
      throws GeneralSecurityException {
  init();

  bek_ = info.getSecretKey(kdk, bea_);
    }

    /**
     * Initializes this instance for encryption/decryption with the given secret
     * key.
     *
     * @param key
     *                The secret key that is used to decrypt. The key must match
     *                the algorithm defined in the
     *                contentEncryptionAlgorithmIdentifier.
     * @throws GeneralSecurityException
     *                 if some cipher related exception is thrown by the
     *                 underlying engines.
     */
    public void init(SecretKey key) throws GeneralSecurityException {
  if (key == null) {
      throw new NullPointerException("Need a SecretKey!");
  }
  init();

  bek_ = key;
    }

    /**
     * Basic initialization.
     */
    protected void init() throws GeneralSecurityException {

  if (params_ == null) {
      params_ = cAlg_.getParameters();
  }
  if (bea_ == null) {
      bea_ = cAlg_.getAlgorithmName();
  }
  if (bea_ == null) {
      throw new NoSuchAlgorithmException("Cannot resolve OID "
        + cAlg_.getAlgorithmOID());
  }
    }

    /**
     * This method returns <code>true</code> if this instance is ready for
     * encryption/decryption without further initialization.
     *
     * @return <code>true</code> if it is ready.
     */
    public boolean isReady() {
  if (bek_ == null) {
      return false;
  }
  return true;
    }

    /**
     * This method initializes and returns a new {@link RecipientInfo
     * RecipientInfo} based on the given certificate. The BEK must already be
     * initialized, otherwise an exception is thrown.
     *
     * @param cert
     *                the certificate
     * @return the created {@link RecipientInfo}
     * @throws GeneralSecurityException
     *                 if some cipher operation fails.
     * @throws BadNameException
     *                 if the issuer name in the given certificate cannot be
     *                 parsed.
     * @throws IllegalStateException
     *                 if the BEK is not yet initialized.
     */
    public RecipientInfo newRecipient(X509Certificate cert)
      throws GeneralSecurityException, BadNameException {
  // FIXME Add and adjust saftey check for new Name implementation of
  // Fraunhofer IGD
  // if(!Name.defaultEncoding_)
  // throw new BadNameException("Use the constructor that explicitly sets
  // the Name encoding");
  if (bek_ == null) {
      throw new IllegalStateException("Not initialized!");
  }
  return new RecipientInfo(cert, bek_);
    }

    /**
     * This method initializes and returns a new {@link RecipientInfo
     * RecipientInfo} based on the given certificate and encoding type. The BEK
     * must already be initialized, otherwise an exception is thrown.
     *
     * @param cert
     *                the certificate
     * @param encType
     *                the encoding type
     * @return the created {@link RecipientInfo}
     * @throws GeneralSecurityException
     *                 if some cipher operation fails.
     * @throws BadNameException
     *                 if the issuer name in the given certificate cannot be
     *                 parsed.
     * @throws IllegalStateException
     *                 if the BEK is not yet initialized.
     */
    public RecipientInfo newRecipient(X509Certificate cert, int encType)
      throws GeneralSecurityException, BadNameException {
  if (bek_ == null) {
      throw new IllegalStateException("Not initialized!");
  }
  return new RecipientInfo(cert, bek_, encType);
    }

    /**
     * Encrypts the given data and inserts it as {@link Data Data} content. The
     * input stream is not closed.
     *
     * @throws IllegalStateException
     *                 if the DEK is not initialized.
     * @throws GeneralSecurityException
     *                 if something nasty happens while encrypting such as
     *                 algorithms not found, bad paddings et cetera.
     */
    public void setData(InputStream in) throws IOException,
      GeneralSecurityException {
  ByteArrayOutputStream out;
  byte[] b;

  out = new ByteArrayOutputStream();
  crypt(in, out, Cipher.ENCRYPT_MODE);

  b = out.toByteArray();
  out.close();

  contentType_ = new ASN1ObjectIdentifier(DATA_OID);
  econtent_ = new ASN1TaggedType(0, new ASN1OctetString(b), false, false);

  clear();
  add(contentType_);
  add(cAlg_);
  add(econtent_);
  trimToSize();
    }

    /**
     * This method decrypts and returns the decrypted data contained in this
     * instance or <code>null</code> if there is no contained data.
     *
     * @throws InconsistentStateException
     *                 in case of an unexpected internal exception. This should
     *                 never happen.
     * @throws IllegalStateException
     *                 if the DEK is not initialized.
     * @throws NoSuchElementException
     *                 if the content type is not {@link Data Data}.
     * @throws GeneralSecurityException
     *                 if a cipher operation fails.
     */
    public byte[] getData() throws GeneralSecurityException,
      NoSuchElementException {
  byte[] b;
  ByteArrayOutputStream out;

  if (!Arrays.equals(contentType_.getOID(), DATA_OID)) {
      throw new NoSuchElementException("Content type is not Data!");
  }
  b = getEncryptedContent();

  if (b == null || b.length == 0) {
      return null;
  }
  try {
      out = new ByteArrayOutputStream();
      crypt(b, out, Cipher.DECRYPT_MODE);
      b = out.toByteArray();
      out.close();

      return b;
  } catch (IOException e) {
      throw new IllegalStateException(e.getMessage());
  }
    }

    /**
     * @param opmode
     *                The operation mode of the cipher.
     * @return A <code>Cipher</code> instance readily initialized for the
     *         given operation mode.
     * @throws GeneralSecurityException
     *                 if something is wrong with the cipher initialization,
     *                 e.g. no bea_ algorithm was found.
     * @throws IllegalStateException
     *                 if this instance is not initialized properly for cipher
     *                 operations. This happens for instance if no secret key
     *                 was set, yet.
     */
    private Cipher createCipher(int opmode) throws GeneralSecurityException {
  Cipher cipher;

  if (bek_ == null) {
      throw new IllegalStateException("No secret key");
  }
  if (bea_ == null) {
      throw new IllegalStateException("No cipher algorithm!");
  }
  cipher = Cipher.getInstance(bea_);

  if (params_ == null) {
      cipher.init(opmode, bek_);
  } else {
      cipher.init(opmode, bek_, params_);
  }
  return cipher;
    }

    /**
     * Pipes the input to the output while encrypting or decrypting the piped
     * data with the BEK. The output stream is not closed by this method but the
     * input stream is.
     *
     * @param in
     *                The stream from which data is read.
     * @param out
     *                The stream to which data is written.
     * @param opmode
     *                The operation mode of the cipher, either
     *                <code>Cipher.ENCRYPT_MODE</code> or
     *                <code>Cipher.DECRYPT_MODE</code>.
     * @throws GeneralSecurityException
     *                 if the some cipher operation caused an exception.
     * @throws IllegalStateException
     *                 if the BEK is not initialized.
     * @throws IOException
     *                 if some I/O error is detected.
     */
    public void crypt(InputStream in, OutputStream out, int opmode)
      throws IOException, GeneralSecurityException {
  Cipher cipher;
  byte[] b;
  int n;

  cipher = createCipher(opmode);
  b = new byte[BUFFER_SIZE];

  while ((n = in.read(b)) > 0) {
      out.write(cipher.update(b, 0, n));
  }
  out.write(cipher.doFinal());
  out.flush();
  in.close();
    }

    /**
     * Crypts or decrypts the given input bytes and writes the resulting cipher
     * text or clear text tp the given output stream. The output stream is
     * flushed but not closed by this method.
     *
     * @param in
     *                The byte array from which data is taken.
     * @param out
     *                The stream to which data is written.
     * @param opmode
     *                The operation mode of the cipher, either
     *                <code>Cipher.ENCRYPT_MODE</code> or
     *                <code>Cipher.DECRYPT_MODE</code>.
     * @throws GeneralSecurityException
     *                 if the some cipher operation caused an exception.
     * @throws IllegalStateException
     *                 if the BEK is not initialized.
     * @throws IOException
     *                 if some I/O error is detected.
     */
    public void crypt(byte[] in, OutputStream out, int opmode)
      throws IOException, GeneralSecurityException {
  Cipher cipher;

  cipher = createCipher(opmode);

  out.write(cipher.doFinal(in));
  out.flush();
    }

    /**
     * Crypts or decrypts the given input bytes and returns the resulting cipher
     * text or clear text.
     *
     * @param in
     *                The byte array from which data is taken.
     * @param offset
     *                The offset in the byte array at which the data starts.
     * @param length
     *                The number of bytes to operate on starting at the given
     *                offset.
     * @param opmode
     *                The operation mode of the cipher, either
     *                <code>Cipher.ENCRYPT_MODE</code> or
     *                <code>Cipher.DECRYPT_MODE</code>.
     *
     * @return The resulting cipher text or clear text depending on the
     *         operation mode.
     *
     * @throws GeneralSecurityException
     *                 if the some cipher operation caused an exception.
     * @throws IllegalStateException
     *                 if the BEK is not initialized.
     */
    public byte[] crypt(byte[] in, int offset, int length, int opmode)
      throws GeneralSecurityException {
  Cipher cipher;

  cipher = createCipher(opmode);

  return cipher.doFinal(in, offset, length);
    }

    /**
     * Decodes this instance with the given decoder. After decoding, an attempt
     * is made to resolve the algorithm name and parameters.
     *
     * @param dec
     *                The decoder to use.
     */
    public void decode(Decoder dec) throws IOException, ASN1Exception {
  super.decode(dec);

  try {
      init();
  } catch (GeneralSecurityException e) {
      /*
       * We ignore this exception at this point. It will be thrown again
       * when this structure is initialized with a key, or algorithm names
       * or parameters are requested.
       */
  }
    }

    /**
     * Encrypts the given data and embeds it into this instance. The content
     * type is set to the specified OID.
     *
     * @param oid
     *                The OID that identifies the content type.
     * @param in
     *                The stream from which the data is read.
     *
     * @throws IllegalStateException
     *                 if this instance is not properly initialized for
     *                 encryption.
     * @throws GeneralSecurityException
     *                 if something nasty happens while encrypting such as
     *                 algorithms not found, bad paddings et cetera.
     */
    public void setEncryptedContent(ASN1ObjectIdentifier oid, InputStream in)
      throws IOException, GeneralSecurityException {
  ByteArrayOutputStream out;
  byte[] b;

  if (oid == null || in == null) {
      throw new NullPointerException("oid or input stream");
  }
  /*
   * Encrypt the data
   */
  out = new ByteArrayOutputStream();

  crypt(in, out, Cipher.ENCRYPT_MODE);

  b = out.toByteArray();

  out.close();

  /*
   * Set the content type
   */
  contentType_ = oid;

  /*
   * Embed the content into this structure.
   */
  econtent_ = new ASN1TaggedType(0, new ASN1OctetString(b), false, false);

  /*
   * Re-build the structure.
   */
  clear();
  add(contentType_);
  add(cAlg_);
  add(econtent_);
  trimToSize();
    }

    /**
     * Sets the content type to the given OID. The OID is copied by reference.
     * Modifying it afterwards causes side effects.
     *
     * @param oid
     *                The OID that identifies the content type.
     */
    public void setContentType(ASN1ObjectIdentifier oid) {
  if (oid == null) {
      throw new NullPointerException("oid");
  }
  contentType_ = oid;
  set(0, contentType_);
    }

}
TOP

Related Classes of codec.pkcs7.EncryptedContentInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.