All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unbound.provider.SecretKeyCipher Maven / Gradle / Ivy

package com.unbound.provider;

import com.dyadicsec.provider.KeyGenSpec;
import com.dyadicsec.provider.KeyParameters;
import com.unbound.common.Log;
import com.unbound.provider.kmip.KMIP;
import com.unbound.provider.kmip.attribute.CryptoParams;
import com.unbound.provider.kmip.attribute.KeyWrappingSpec;
import com.unbound.provider.kmip.attribute.MessageExt;
import com.unbound.provider.kmip.object.PrivateKey;
import com.unbound.provider.kmip.object.SymmetricKey;
import com.unbound.provider.kmip.request.DecryptRequest;
import com.unbound.provider.kmip.request.EncryptRequest;
import com.unbound.provider.kmip.request.GetRequest;
import com.unbound.provider.kmip.request.RegisterRequest;
import com.unbound.provider.kmip.response.DecryptResponse;
import com.unbound.provider.kmip.response.EncryptResponse;
import com.unbound.provider.kmip.response.GetResponse;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.IOException;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.ArrayList;
import java.util.Arrays;


public class SecretKeyCipher extends CipherSpi
{
  private KeyParameters unwrapKeyParameter = null;
  private int kmipAlg;
  private boolean aad = false;
  private boolean wrap = false;
  private boolean encrypt = false;
  private boolean padding = false;
  private int kmipMode = 0;
  private AlgorithmParameterSpec paramSpec = null;
  private byte[] auth = null;
  private byte[] corr = null;
  private UBSecretKey secretKey = null;
  private CryptoParams kmipParams = new CryptoParams();
  private MessageExt kmipExt = null;

  SecretKeyCipher(int kmipAlg)
  {
    this.kmipAlg = kmipAlg;
  }

  private void init()
  {
    auth = null;
    corr = null;
  }

  private void setKmipIV(SecureRandom secureRandom) throws InvalidAlgorithmParameterException
  {
    if (paramSpec==null && encrypt)
    {
      if (secureRandom==null) throw new InvalidAlgorithmParameterException("Can't generate IV");
      int size = engineGetBlockSize();
      byte[] ivData = new byte[size];
      secureRandom.nextBytes(ivData);
      paramSpec = new IvParameterSpec(ivData);
    }
    if (paramSpec==null || !(paramSpec instanceof IvParameterSpec)) throw new InvalidAlgorithmParameterException("IvParameterSpec required");
    IvParameterSpec ivSpec = (IvParameterSpec)paramSpec;
    kmipExt = new MessageExt();
    kmipExt.iv = ivSpec.getIV();
  }

  private ArrayList getAuthParam()
  {
    if (!aad) return null;
    if (auth==null) auth = new byte[0];
    ArrayList a = new ArrayList();
    a.add(auth);
    return a;
  }

  private int execEncDec(byte[] in, int inOffset, int inLen, byte[] out, int outOffset, boolean doFinal) throws ShortBufferException
  {
    int outLen = -1;
    Log log = Log.func("SecretKeyCipher.execEncDec")
      .log("encrypt", encrypt)
      .log("doFinal", doFinal)
      .log("inOffset", inOffset)
      .log("inLen", inLen).end(); try
    {
      if (secretKey==null) throw new ProviderException("Not initialized");

      byte[] result;
      if (encrypt)
      {
        EncryptRequest req = new EncryptRequest();
        req.corr = corr;
        if (inLen>0) req.data = Arrays.copyOfRange(in, inOffset, inOffset+inLen);
        req.initInd = corr==null;
        req.finalInd = doFinal;
        req.uid = UBObject.uidToStr(secretKey.uid);
        req.params = kmipParams;
        req.ext = kmipExt;
        if (kmipExt!=null)
        {
          req.ext.auth = getAuthParam();
          req.iv = kmipExt.iv;
        }
        EncryptResponse resp;
        try
        {
          resp = (EncryptResponse) secretKey.partition.transmit(req);
        }
        catch (IOException e)
        {
          throw new ProviderException(e);
        }
        corr = resp.corr;
        result = resp.data;
      }
      else
      {
        DecryptRequest req = new DecryptRequest();
        req.corr = corr;
        if (inLen>0) req.data = Arrays.copyOfRange(in, inOffset, inOffset+inLen);
        req.initInd = corr==null;
        req.finalInd = doFinal;
        req.uid = UBObject.uidToStr(secretKey.uid);
        req.params = kmipParams;
        req.ext = kmipExt;
        if (kmipExt!=null)
        {
          req.ext.auth = getAuthParam();
          req.iv = kmipExt.iv;
        }
        DecryptResponse resp;
        try
        {
          resp = (DecryptResponse) secretKey.partition.transmit(req);
        }
        catch (IOException e)
        {
          init();
          throw new ProviderException(e);
        }
        corr = resp.corr;
        result = resp.data;
      }

      if (result.length > out.length-outOffset) { init(); throw new ShortBufferException(); }
      System.arraycopy(result, 0, out, outOffset, result.length);
      outLen = result.length;
      return outLen;
    }
    catch (Exception e) { log.failed(e); throw e; } finally { log.leavePrint().log("outLen", outLen).end(); }
  }

  // ------------------------ interface ----------------------------

  @Override
  protected void engineSetMode(String mode) throws NoSuchAlgorithmException
  {
    aad = false;
    mode = mode.toUpperCase();

         if (mode.equalsIgnoreCase("GCM"))    { kmipMode = KMIP.BlockCipherMode.GCM;  aad = true; }
    else if (mode.equalsIgnoreCase("ECB"))    { kmipMode = KMIP.BlockCipherMode.ECB; }
    else if (mode.equalsIgnoreCase("CBC"))    { kmipMode = KMIP.BlockCipherMode.CBC; }
    else if (mode.equalsIgnoreCase("CTR"))    { kmipMode = KMIP.BlockCipherMode.CTR; }
    else if (mode.equalsIgnoreCase("OFB128")) { kmipMode = KMIP.BlockCipherMode.OFB; }
    else if (mode.equalsIgnoreCase("CFB128")) { kmipMode = KMIP.BlockCipherMode.CFB; }
    else throw new NoSuchAlgorithmException("Mode not supported: " + mode);
  }

  @Override
  protected void engineSetPadding(String padding) throws NoSuchPaddingException
  {
    if (padding.equalsIgnoreCase("NOPADDING")) this.padding = false;
    else if (padding.equalsIgnoreCase("PKCS5PADDING"))
    {
      if (kmipMode!=KMIP.BlockCipherMode.CBC) throw new NoSuchPaddingException("padding not supported");
      this.padding = true;
    }
    else throw new NoSuchPaddingException("padding not supported");
  }

  @Override
  protected int engineGetBlockSize()
  {
    return 16; // AES only
  }

  @Override
  protected int engineGetOutputSize(int inputLen)
  {
    if (kmipMode==KMIP.BlockCipherMode.GCM)
    {
      int tlen = ((GCMParameterSpec)paramSpec).getTLen()/8;
      return encrypt ? (inputLen + tlen) : (inputLen - tlen);
    }
    return inputLen;
  }

  @Override
  protected byte[] engineGetIV()
  {
    if (paramSpec==null) return null;
    if (paramSpec instanceof IvParameterSpec) return ((IvParameterSpec)paramSpec).getIV();
    return null;
  }

  @Override
  protected AlgorithmParameters engineGetParameters()
  {
    if (paramSpec==null) return null;

    try
    {
      AlgorithmParameters params = AlgorithmParameters.getInstance("AES", "SunJCE");
      params.init(paramSpec);
      return params;
    }
    catch (GeneralSecurityException e) { throw new ProviderException("Could not encode parameters", e); }
  }

  @Override
  protected void engineInit(int opmode, Key key, AlgorithmParameterSpec algorithmParameterSpec, SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException
  {
    if (algorithmParameterSpec instanceof KeyGenSpec)
    {
      if (opmode!=Cipher.UNWRAP_MODE) throw new InvalidAlgorithmParameterException("KeyParameter is supported only in UNWRAP_MODE");
      unwrapKeyParameter = ((KeyGenSpec)algorithmParameterSpec).getKeyParams();
      algorithmParameterSpec = ((KeyGenSpec)algorithmParameterSpec).getOriginal();
    }
    else unwrapKeyParameter = null;

    this.secretKey = null;
    init();

    if (opmode!=Cipher.WRAP_MODE &&
        opmode!=Cipher.UNWRAP_MODE &&
        opmode!=Cipher.ENCRYPT_MODE &&
        opmode!=Cipher.DECRYPT_MODE)  throw new InvalidParameterException("Invalid mode");

    if (!(key instanceof UBSecretKey)) throw new InvalidKeyException("Invalid key type");
    UBSecretKey secretKey = (UBSecretKey)key;
    if (secretKey.getKmipAlg()!=KMIP.CryptographicAlgorithm.AES) throw new InvalidKeyException("Invalid key type");

    wrap = (opmode == Cipher.WRAP_MODE) || (opmode == Cipher.UNWRAP_MODE);
    encrypt = (opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE);
    paramSpec = algorithmParameterSpec;

    kmipParams.mode = kmipMode;
    kmipParams.padding = padding ? KMIP.PaddingMethod.PKCS5 : KMIP.PaddingMethod.None;

    GCMParameterSpec gcm;
    int tagBits;

    switch (kmipMode)
    {
      case KMIP.BlockCipherMode.GCM:
        if (!(paramSpec instanceof GCMParameterSpec)) throw new InvalidAlgorithmParameterException("GCMParameterSpec required");
        gcm = (GCMParameterSpec) paramSpec;
        kmipExt = new MessageExt();
        kmipExt.iv = gcm.getIV();
        tagBits = gcm.getTLen();
        if ((tagBits % 8)!=0) throw new InvalidAlgorithmParameterException("Invalid tag length");
        kmipParams.tagLength = tagBits / 8;
        if (kmipParams.tagLength<1 || kmipParams.tagLength>16) throw new InvalidAlgorithmParameterException("Invalid tag length");
        if (kmipExt.iv.length!=12) throw new InvalidAlgorithmParameterException("Invalid IV length");
        break;

      case KMIP.BlockCipherMode.CTR:
        setKmipIV(secureRandom);
        if (kmipExt.iv.length!=16) throw new InvalidAlgorithmParameterException("Invalid IV length");
        kmipParams.counterLength = 32;
        break;

      case KMIP.BlockCipherMode.CBC:
      case KMIP.BlockCipherMode.OFB:
      case KMIP.BlockCipherMode.CFB:
        setKmipIV(secureRandom);
        if (kmipExt.iv.length!=16) throw new InvalidAlgorithmParameterException("Invalid IV length");
        break;
    }

    this.secretKey = secretKey;
  }

  @Override
  protected void engineInit(int opmode, Key key, AlgorithmParameters algorithmParameters, SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException
  {
    AlgorithmParameterSpec spec = null;

    Class clazz = IvParameterSpec.class;
    if (kmipMode==KMIP.BlockCipherMode.GCM) clazz = GCMParameterSpec.class;

    if (algorithmParameters!=null)
    {
      try { spec = algorithmParameters.getParameterSpec(clazz); }
      catch (InvalidParameterSpecException ipse) { throw new InvalidAlgorithmParameterException("Wrong parameter"); }
    }

    engineInit(opmode, key, spec, secureRandom);
  }

  @Override
  protected void engineInit(int opmode, Key key, SecureRandom secureRandom) throws InvalidKeyException
  {
    try { engineInit(opmode, key, (AlgorithmParameterSpec)null, secureRandom); }
    catch (InvalidAlgorithmParameterException e) { throw new InvalidKeyException(e); }
  }

  @Override
  protected void engineUpdateAAD(byte[] in, int inOffset, int inLen) throws IllegalStateException, UnsupportedOperationException
  {
    if (!aad) throw new IllegalStateException("Cipher does not accept AAD");
    if (auth==null)
    {
      auth = Arrays.copyOfRange(in, inOffset, inOffset+inLen);
    }
    else
    {
      int oldSize = auth.length;
      byte[] newBuffer = new byte[oldSize+inLen];
      if (oldSize>0) System.arraycopy(auth, 0, newBuffer, 0, oldSize);
      System.arraycopy(in, inOffset, newBuffer, oldSize, inLen);
      auth = newBuffer;
    }
  }

  @Override
  protected byte[] engineUpdate(byte[] in, int inOffset, int inLen)
  {
    byte[] out = new byte[inLen+32];
    int outLen = 0;
    try { outLen = engineUpdate(in, inOffset, inLen, out, 0); }
    catch (ShortBufferException e) { throw new ProviderException(e); }
    return Arrays.copyOf(out, outLen);
  }

  @Override
  protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen) throws IllegalBlockSizeException, BadPaddingException
  {
    byte[] out = new byte[inLen+32];
    int outLen = 0;
    try { outLen = engineDoFinal(in, inOffset, inLen, out, 0); }
    catch (ShortBufferException e) { throw new ProviderException(e); }
    return Arrays.copyOf(out, outLen);
  }

  @Override
  protected int engineUpdate(byte[] in, int inOffset, int inLen, byte[] out, int outOffset) throws ShortBufferException
  {
    if (inLen==0) return 0;
    return execEncDec(in, inOffset, inLen, out, outOffset, false);
  }

  @Override
  protected int engineDoFinal(byte[] in, int inOffset, int inLen, byte[] out, int outOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
  {
    if (inLen==0)
    {
      switch (kmipMode)
      {
        case KMIP.BlockCipherMode.OFB:
        case KMIP.BlockCipherMode.CFB:
        case KMIP.BlockCipherMode.CTR:
        case KMIP.BlockCipherMode.ECB:
          return 0;

        case KMIP.BlockCipherMode.CBC:
          if (!padding) return 0;
          break;
      }
    }
    int outLen = execEncDec(in, inOffset, inLen, out, outOffset, true);
    init();
    return outLen;
  }

  @Override
  protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException
  {
    Log log = Log.func("SecretKeyCipher.engineWrap").end(); try
    {
      if (secretKey==null) throw new ProviderException("Not initialized");
      if (key==null) throw new ProviderException("key is null");
      if (corr!=null) throw new ProviderException("engineUpdate called");

      if (key instanceof UBSecretKey) ;
      else if (key instanceof UBRSAPrivateKey) ;
      else
      {
        byte[] encoded = key.getEncoded();
        try { return engineDoFinal(encoded, 0, encoded.length); }
        catch (BadPaddingException e) { throw new ProviderException(e); }
      }

      UBObject ubObject = (UBObject)key;

      GetRequest req = new GetRequest();
      req.uid = UBObject.uidToStr(ubObject.uid);
      req.formatType = KMIP.KeyFormatType.Raw;
      req.keyWrap = new KeyWrappingSpec();
      req.keyWrap.encKey.uid = UBObject.uidToStr(secretKey.uid);
      req.keyWrap.encKey.params = kmipParams;
      req.ext = kmipExt;
      if (kmipExt!=null) req.ext.auth = getAuthParam();
      GetResponse resp;
      try
      {
        resp = (GetResponse) secretKey.partition.transmit(req);
      }
      catch (IOException e)
      {
        throw new ProviderException(e);
      }
      init();
      if (resp.object instanceof PrivateKey) return ((PrivateKey)resp.object).keyBlock.buf;
      if (resp.object instanceof SymmetricKey) return ((SymmetricKey)resp.object).keyBlock.buf;
      throw new ProviderException("Invalid managed object returned");
    }
    catch (Exception e) { log.failed(e); throw e; } finally { log.leave(); }
  }

  @Override
  protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException,  NoSuchAlgorithmException
  {
    Log log = Log.func("SecretKeyCipher.engineUnwrap")
      .log("wrappedKey.length", wrappedKey.length)
      .log("wrappedKeyAlgorithm", wrappedKeyAlgorithm)
      .log("wrappedKeyType", wrappedKeyType).end(); try
    {
      if (secretKey==null) throw new ProviderException("Not initialized");
      if (corr!=null) throw new ProviderException("engineUpdate called");

      int kmipAlg = 0;
      UBPrivateKey newPrivateKey;
      UBSecretKey newSecretKey;

      RegisterRequest registerRequest = new RegisterRequest();
      registerRequest.ext = kmipExt;
      if (kmipExt!=null) registerRequest.ext.auth = getAuthParam();

      switch (wrappedKeyType)
      {
        case Cipher.PRIVATE_KEY:
          if (wrappedKeyAlgorithm.equalsIgnoreCase("RSA")) newPrivateKey = new UBRSAPrivateKey(secretKey.partition);
          else if (wrappedKeyAlgorithm.equalsIgnoreCase("EC")) newPrivateKey = new UBECPrivateKey(secretKey.partition);
          else throw new InvalidKeyException("Unsupported wrappedKeyAlgorithm " + wrappedKeyAlgorithm);
          try { newPrivateKey.unwrap(unwrapKeyParameter, registerRequest, secretKey.uid, kmipParams, wrappedKey); }
          catch (IOException | InvalidKeySpecException e) { throw new ProviderException(e); }
          return newPrivateKey;

        case Cipher.SECRET_KEY:
          if (wrappedKeyAlgorithm.equalsIgnoreCase("AES")) kmipAlg = KMIP.CryptographicAlgorithm.AES;
          else if (wrappedKeyAlgorithm.equalsIgnoreCase("HMAC")) kmipAlg = KMIP.CryptographicAlgorithm.HMAC_SHA256;
          else throw new InvalidKeyException("Unsupported wrappedKeyAlgorithm " + wrappedKeyAlgorithm);
          newSecretKey = new UBSecretKey(secretKey.partition, kmipAlg);
          try { newSecretKey.unwrap(unwrapKeyParameter, registerRequest, secretKey.uid, kmipParams, wrappedKey); }
          catch (IOException e) { throw new ProviderException(e); }
          return newSecretKey;
      }
      throw new InvalidKeyException("Unsupported wrappedKeyType");
    }
    catch (Exception e) { log.failed(e); throw e; } finally { log.leave(); }
  }


  // ----------------------------- Sub-classes -----------------------

  public static final class AES extends SecretKeyCipher
  {
    public AES() { super(KMIP.CryptographicAlgorithm.AES); }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy