/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.asn;

import org.cryptacular.EncodingException;
import org.cryptacular.util.PemUtil;

/**
 * Base class for all private key decoders.
 *
 * @param  <T>  Type produced by decode operation.
 *
 * @author  Middleware Services
 */
public abstract class AbstractPrivateKeyDecoder<T> implements ASN1Decoder<T>
{

  @Override
  public T decode(final byte[] encoded, final Object... args) throws EncodingException
  {
    try {
      final byte[] asn1Bytes;
      if (args != null && args.length > 0 && args[0] instanceof char[]) {
        asn1Bytes = decryptKey(encoded, (char[]) args[0]);
      } else {
        asn1Bytes = tryConvertPem(encoded);
      }
      return decodeASN1(asn1Bytes);
    } catch (EncodingException e) {
      throw e;
    } catch (RuntimeException e) {
      throw new EncodingException("Key encoding error", e);
    }
  }


  /**
   * Tests the given encoded input and converts it to PEM if it is detected, stripping out any header/footer data in the
   * process.
   *
   * @param  input  Encoded data that may be PEM encoded.
   *
   * @return  Decoded data if PEM encoding detected, otherwise original data.
   */
  protected byte[] tryConvertPem(final byte[] input)
  {
    if (PemUtil.isPem(input)) {
      return PemUtil.decode(input);
    }
    return input;
  }


  /**
   * Decrypts an encrypted key in either PKCS#8 or OpenSSL "traditional" format. Both PEM and DER encodings are
   * supported.
   *
   * @param  encrypted  Encoded encrypted key data.
   * @param  password  Password to decrypt key.
   *
   * @return  Decrypted key.
   */
  protected abstract byte[] decryptKey(byte[] encrypted, char[] password);


  /**
   * Decodes the given raw ASN.1 encoded data into a private key of the type supported by this class.
   *
   * @param  encoded  Encoded ASN.1 data.
   *
   * @return  Private key object.
   */
  protected abstract T decodeASN1(byte[] encoded);
}
