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

com.dyadicsec.advapi.SDESessionKey Maven / Gradle / Ivy

Go to download

This is a collection of JAVA libraries that implement Unbound cryptographic classes for JAVA provider, PKCS11 wrapper, cryptoki, and advapi

There is a newer version: 42761
Show newest version
package com.dyadicsec.advapi;

import com.dyadicsec.cryptoki.*;
import com.unbound.client.CipherMode;
import com.unbound.client.CipherOper;
import com.unbound.client.Client;
import com.unbound.client.pkcs11.PKCS11SecretKey;
import com.unbound.client.pkcs11.PKCS11Session;
import com.unbound.common.Converter;
import com.unbound.common.crypto.FPE;
import com.unbound.common.crypto.HMAC;
import com.unbound.common.crypto.SPE;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.ProviderException;
import java.sql.SQLException;
import java.time.*;
import java.util.Arrays;
import java.util.Calendar;
import java.util.TimeZone;

/**
 * This class includes methods for application level encryption using a derived data encryption key (DEK), see Application-Level Encryption in the UKC Developers Guide for more information.
 */
public class SDESessionKey
{
  final private int purpose;
  final SDEKey sdeKey;
  PKCS11SecretKey secretKey = null;
  final byte[] rawKey;

  SDESessionKey(SDEKey sdeKey, int purpose, PKCS11SecretKey secretKey)
  {
    this.rawKey = null;
    this.secretKey = secretKey;
    this.sdeKey = sdeKey;
    this.purpose = purpose;
  }

  SDESessionKey(SDEKey sdeKey, int purpose, byte[] rawKey)
  {
    this.rawKey = rawKey;
    this.sdeKey = sdeKey;
    this.purpose = purpose;
  }

  @Override
  protected void finalize()
  {
    destroy();
  }

  public void destroy()
  {
    if (secretKey==null) return;
    secretKey.delete();
    secretKey = null;
  }

  public byte[] getKeyMaterial()
  {
    if (rawKey!=null) return rawKey;

    PKCS11Session session=null;
    try
    {
      session = (PKCS11Session)sdeKey.prfKey.getPartition().acquireSession();
      return secretKey.getValue(session);
    }
    finally { if (session!=null) session.release(); }
  }

  /**
   * Get the SDEKey used to derive this date encryption key
   *
   * @return The SDEKey used for this data encryption key derivation
   */
  public SDEKey getSDEKey()
  {
    return sdeKey;
  }

  private static final int ONEWAY_TOKEN_SIZE = 16;

  /**
   * Creates a unique searchable token from a byte array
   *
   * @param in The input data
   * @return Searchable token of size 16 bytes
   * @throws SecurityException In case of encryption error
   */
  public byte[] encryptPRF(byte[] in) throws SecurityException
  {
    if (purpose != SDEKey.PURPOSE_ONE_WAY) throw new IllegalArgumentException("Invalid purpose");
    return HMAC.SHA256.hmac(getKeyMaterial(), in);
  }

  /**
   * Creates a unique searchable token from a byte array
   *
   * @param data The input data
   * @return Searchable token of size 16 bytes
   * @throws SecurityException In case of encryption error
   */
  public String encryptPRF(final String data) throws SecurityException
  {
    byte[] encData = encryptPRF(data.getBytes(StandardCharsets.UTF_8));
    return SDEUtils.bytesToStringTP(encData);
  }

  private CipherOper newOperation(CipherMode mode, boolean enc)
  {
    CipherOper oper = Client.getInstance().newCipherOperation();
    oper.encMode = enc;
    oper.mode = mode;
    oper.keyObject = secretKey;
    return oper;
  }

  private byte[] encryptTypePreserving(byte[] in, int bits) throws SecurityException
  {
    if (purpose != SDEKey.PURPOSE_SP_ENC) throw new IllegalArgumentException("Invalid purpose");
    if ((in.length < 16) && ((bits % 2) != 0) && bits != 1)
      throw new IllegalArgumentException("Byte array input for type preserving encryption cannot be odd and less then 16 bytes");

    if (rawKey!=null) return SPE.encrypt(rawKey, in, bits);

    CipherOper oper = newOperation(CipherMode.SPE, true);
    oper.speBits = bits;
    return oper.enc(in);
  }

  /**
   * Encrypt a byte array
   *
   * @param in The data to encrypt, where the length of the array should be even or at least 16 bytes
   * @return The encrypted value, where the size of the encrypted data equals to the size of the input
   * @throws SecurityException In case of encryption error
   */
  public byte[] encryptTypePreserving(byte[] in) throws SecurityException
  {
    return encryptTypePreserving(in, in.length * 8);
  }

  private byte[] decryptTypePreserving(byte[] in, int bits) throws SecurityException
  {
    if (purpose != SDEKey.PURPOSE_SP_ENC) throw new IllegalArgumentException("Invalid purpose");
    if ((in.length < 16) && ((bits % 2) != 0) && bits != 1)
      throw new IllegalArgumentException("Byte array input for type preserving decryption cannot be odd and less then 16 bytes");

    if (rawKey!=null) return SPE.decrypt(rawKey, in, bits);

    CipherOper oper = newOperation(CipherMode.SPE, false);
    oper.keyObject = secretKey;
    oper.speBits = bits;
    return oper.dec(in);
  }

  /**
   * Decrypt a byte array
   *
   * @param in The encrypted data
   * @return Decrryped byte array
   * @throws SecurityException In case of decryption error
   */
  public byte[] decryptTypePreserving(byte[] in) throws SecurityException
  {
    return decryptTypePreserving(in, in.length * 8);
  }


  /**
   * Encrypt a string value. The string must be Unicode (as defined by ISO/IEC 10646).
   *
   * @param in      The data to encrypt
   * @param BMPOnly Determines if the plain and cipher text includes only Unicode Basic Multilingual Plane (BMP) codes
   *                or all Unicode planes. BMP should be suitable for most of the use cases and
   *                has a more compact byte representation. Set to false if the full set of Unicode codes is
   *                required. For more information on Unicode planes,
   *                see https://en.wikipedia.org/wiki/Plane_(Unicode)
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public String encryptTypePreserving(String in, boolean BMPOnly) throws SecurityException
  {
    if (purpose != SDEKey.PURPOSE_STRING_ENC) throw new IllegalArgumentException("Invalid purpose");

    if (rawKey!=null)
    {
      return FPE.STR.encrypt(rawKey, in, BMPOnly);
    }

    String format = BMPOnly ? "ISO-10646-UCS-2" : "UTF-16BE";
    CipherOper oper = newOperation(CipherMode.FPE, true);
    oper.fpeFormat = format;
    oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_STRING;
    byte[] inBuf = in.getBytes(StandardCharsets.UTF_16BE);
    byte[] outBuf = oper.enc(inBuf);
    return new String(outBuf, StandardCharsets.UTF_16BE);
  }

  /**
   * Decrypt a string value
   *
   * @param in      The data to decrypt
   * @param BMPOnly Determines if the plain and cipher text includes only Unicode Basic Multilingual Plane (BMP) codes
   *                or all Unicode planes. BMP should be suitable for most of the use cases and
   *                has a more compact byte representation. Set to false if the full set of Unicode codes is
   *                required. For more information on Unicode planes,
   *                see https://en.wikipedia.org/wiki/Plane_(Unicode)
   * @return Plain value
   * @throws SecurityException In case of encryption error
   */
  public String decryptTypePreserving(String in, boolean BMPOnly) throws SecurityException
  {
    if (purpose != SDEKey.PURPOSE_STRING_ENC) throw new IllegalArgumentException("Invalid purpose");

    if (rawKey!=null)
    {
      return FPE.STR.decrypt(rawKey, in, BMPOnly);
    }

    String format = BMPOnly ? "ISO-10646-UCS-2" : "UTF-16BE";
    CipherOper oper = newOperation(CipherMode.FPE, false);
    oper.fpeFormat = format;
    oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_STRING;
    byte[] inBuf = in.getBytes(StandardCharsets.UTF_16BE);
    byte[] outBuf = oper.dec(inBuf);
    return new String(outBuf, StandardCharsets.UTF_16BE);
  }

  /**
   * Encrypt a string value in order preserving form
   *
   * @param data The data to encrypt
   * @param size Maximum size of values that should be compared with this value
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public String encryptOrderPreserving(String data, int size) throws SecurityException
  {
    if (rawKey!=null) throw new ProviderException("Not supported in clientless mode");

    byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
    dataBytes = SDEUtils.addTailing(dataBytes, (byte) 0, size - dataBytes.length);
    byte[] encData = encryptOPE(dataBytes);
    return SDEUtils.bytesToStringOP(encData);
  }

  /**
   * Decrypt a string value encrypted with order preserving encryption
   *
   * @param encDataStr The encrypted value
   * @return String value in plain
   * @throws SecurityException In case of decryption error
   */
  public String decryptOrderPreserving(String encDataStr) throws SecurityException
  {
    if (rawKey!=null) throw new ProviderException("Not supported in clientless mode");

    byte[] encData = SDEUtils.stringToBytesOP(encDataStr);
    byte[] data = decryptOPE(encData);
    data = SDEUtils.removeTailing(data, (byte) 0);
    return new String(data, StandardCharsets.UTF_8);
  }


  byte[] encryptOPE(byte[] in) throws SecurityException
  {
    if (rawKey!=null) throw new ProviderException("Not supported in clientless mode");

    if (purpose != SDEKey.PURPOSE_OP_ENC) throw new IllegalArgumentException("Invalid purpose");

    CipherOper oper = newOperation(CipherMode.OPE, true);
    return oper.enc(in);
  }

  byte[] decryptOPE(byte[] in) throws SecurityException
  {
    if (rawKey!=null) throw new ProviderException("Not supported in clientless mode");

    if (purpose != SDEKey.PURPOSE_OP_ENC) throw new IllegalArgumentException("Invalid purpose");
    CipherOper oper = newOperation(CipherMode.OPE, false);
    return oper.dec(in);
  }

  /**
   * Encrypt a long value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public long encryptTypePreserving(long data) throws SecurityException
  {
    byte[] dataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(data).array();
    byte[] encData = encryptTypePreserving(dataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(encData); // big-endian by default
    return wrapped.getLong();
  }

  /**
   * Decrypt an encryptyed long value
   *
   * @param encData The encrypted value
   * @return long value in plain
   * @throws SecurityException In case of decryption error
   */
  public long decryptTypePreserving(long encData) throws SecurityException
  {
    byte[] encDataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(encData).array();
    byte[] data = decryptTypePreserving(encDataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(data);
    return wrapped.getLong();
  }

  /**
   * Encrypt a integer value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public int encryptTypePreserving(int data) throws SecurityException
  {
    byte[] dataBytes = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(data).array();
    byte[] encData = encryptTypePreserving(dataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(encData); // big-endian by default
    return wrapped.getInt();
  }

  /**
   * Decrypt an encryptyed integer value
   *
   * @param encData The encrypted value
   * @return long value in plain
   * @throws SecurityException In case of decryption error
   */
  public int decryptTypePreserving(int encData) throws SecurityException
  {
    byte[] encDataBytes = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(encData).array();
    byte[] data = decryptTypePreserving(encDataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(data);
    return wrapped.getInt();
  }

  /**
   * Encrypt an integer value in order preserving form, encrypted value is of type long
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public long encryptOrderPreserving(int data) throws SecurityException
  {
    byte[] dataBytes = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(data).array();
    byte[] encData = encryptOPE(dataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(encData); // big-endian by default
    return wrapped.getLong();
  }

  /**
   * Decrypt an integer value encrypted with order preserving encryption
   *
   * @param encData The encrypted value
   * @return Integer value in plain
   * @throws SecurityException In case of decryption error
   */
  public int decryptOrderPreserving(long encData) throws SecurityException
  {
    byte[] encDataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(encData).array();
    byte[] data = decryptOPE(encDataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(data); // big-endian by default
    return wrapped.getInt();
  }

  /**
   * Encrypt a short value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public short encryptTypePreserving(short data) throws SecurityException
  {
    byte[] dataBytes = ByteBuffer.allocate(Short.SIZE / Byte.SIZE).putShort(data).array();
    byte[] encData = encryptTypePreserving(dataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(encData); // big-endian by default
    return wrapped.getShort();
  }

  /**
   * Decrypt an encryptyed short value
   *
   * @param encData The encrypted value
   * @return String value in plain
   * @throws SecurityException In case of decryption error
   */
  public short decryptTypePreserving(short encData) throws SecurityException
  {
    byte[] encDataBytes = ByteBuffer.allocate(Short.SIZE / Byte.SIZE).putShort(encData).array();
    byte[] data = decryptTypePreserving(encDataBytes);
    ByteBuffer wrapped = ByteBuffer.wrap(data);
    return wrapped.getShort();
  }

  /**
   * Encrypt a short value in order preserving form, where the return value is of type long
   *
   * @param data The value to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public long encryptOrderPreserving(short data) throws SecurityException
  {
    return encryptOrderPreserving((int) data);
  }

  static private boolean isValidFloatBin(int u)
  {
    int man  =  u      & 0x007fffff; // 23 bits
    int exp  = (u>>23) & 0x000000ff; //  8 bits
    int sign = (u>>31) & 0x00000001; //  1 bit

    if (exp == 0)
    {
      if (sign != 0) return false;
      if (man  == 0) return true; //ZERO;
      return false;               //SUBNORMAL;
    }

    if (exp == 0xff)
    {
      if (u==0x7f800000) return true; //POSITIVE_INFINITY
      if (u==0xff800000) return true; //NEGATIVE_INFINITY
      if (u==0x7fc00000) return true; //NaN
      return false;
    }

    return true; //NORMAL;
  }

  static private boolean isValidDoubleBin(long u)
  {
    long man = u & 0x000fffffffffffffL;     // 52 bits
    int exp  = (int)((u>>52) & 0x000007ff); // 11 bits
    int sign = (int)((u>>63) & 0x00000001); //  1 bit

    if (exp == 0)
    {
      if (sign != 0) return false;
      if (man  == 0) return true; //ZERO;
      return false;               //SUBNORMAL;
    }

    if (exp == 0x7ff)
    {
      if (u==0x7ff0000000000000L) return true; //POSITIVE_INFINITY
      if (u==0xfff0000000000000L) return true; //NEGATIVE_INFINITY
      if (u==0x7ff8000000000000L) return true; //NaN
      return false;
    }

    return true; //NORMAL;
  }

  /**
   * Encrypt a float value
   *
   * @param data The value to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public float encryptTypePreserving(float data) throws SecurityException
  {
    int u = Float.floatToRawIntBits(data);
    if (!isValidFloatBin(u)) throw new IllegalArgumentException("Invalid float value");
    byte[] b = new byte[4];
    Converter.setLE4(b, 0, u);

    for (;;)
    {
      b = encryptTypePreserving(b);
      u = Converter.getLE4(b,0);
      if (isValidFloatBin(u)) break;
    }

    return Float.intBitsToFloat(u);
  }

  /**
   * Decrypt an encryptyed float value
   *
   * @param encData The encrypted value
   * @return Float value in plain
   * @throws SecurityException In case of decryption error
   */
  public float decryptTypePreserving(float encData) throws SecurityException
  {
    int u = Float.floatToRawIntBits(encData);
    if (!isValidFloatBin(u)) throw new IllegalArgumentException("Invalid float value");
    byte[] b = new byte[4];
    Converter.setLE4(b, 0, u);

    for (;;)
    {
      b = decryptTypePreserving(b);
      u = Converter.getLE4(b,0);
      if (isValidFloatBin(u)) break;
    }

    return Float.intBitsToFloat(u);
  }

  /**
   * Encrypt a double value
   *
   * @param data The value to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public double encryptTypePreserving(double data) throws SecurityException
  {
    long u = Double.doubleToRawLongBits(data);
    if (!isValidDoubleBin(u)) throw new IllegalArgumentException("Invalid float value");
    byte[] b = new byte[8];
    Converter.setLE8(b, 0, u);

    for (;;)
    {
      b = encryptTypePreserving(b);
      u = Converter.getLE8(b,0);
      if (isValidDoubleBin(u)) break;
    }

    return Double.longBitsToDouble(u);
  }

  /**
   * Decrypt an encryptyed double value
   *
   * @param encData The encrypted value
   * @return Double value in plain
   * @throws SecurityException In case of decryption error
   */
  public double decryptTypePreserving(double encData) throws SecurityException
  {
    long u = Double.doubleToRawLongBits(encData);
    if (!isValidDoubleBin(u)) throw new IllegalArgumentException("Invalid float value");
    byte[] b = new byte[8];
    Converter.setLE8(b, 0, u);

    for (;;)
    {
      b = decryptTypePreserving(b);
      u = Converter.getLE8(b,0);
      if (isValidDoubleBin(u)) break;
    }

    return Double.longBitsToDouble(u);
  }

  static long dateToDays(java.sql.Date date)
  {
    LocalDate local = date.toLocalDate();
    Instant instant = local.atStartOfDay().toInstant(ZoneOffset.UTC);
    Instant epoch = Instant.ofEpochMilli(0);
    Duration duration = Duration.between(epoch, instant);
    return duration.toDays();
  }

  static java.sql.Date daysToDate(long days)
  {
    Duration duration = Duration.ofDays(days);
    Instant inst = Instant.ofEpochMilli(0).plus(duration);
    LocalDate local = LocalDateTime.ofInstant(inst, ZoneOffset.UTC).toLocalDate();
    return java.sql.Date.valueOf(local);
  }


  /**
   * Encrypt a Date value
   *
   * @param plain The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.sql.Date encryptTypePreserving(java.sql.Date plain) throws SecurityException
  {
    long d = dateToDays(plain);

    byte[] dataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(d).array();

    SDEUtils.reverseBytes(dataBytes);
    dataBytes = Arrays.copyOf(dataBytes, 3); // we need only 3 bytes

    byte[] encData = encryptTypePreserving(dataBytes, 20);

    SDEUtils.reverseBytes(encData);
    byte[] longData = new byte[8];
    System.arraycopy(encData, 0, longData, 5, 3);

    ByteBuffer wrapped = ByteBuffer.wrap(longData);
    long output = wrapped.getLong();
    return daysToDate(output);
  }

  /**
   * Decrypt an encryptyed Date value
   *
   * @param enc The encrypted value
   * @return Date value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Date decryptTypePreserving(java.sql.Date enc) throws SecurityException
  {
    long d = dateToDays(enc);
    byte[] encDataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(d).array();

    SDEUtils.reverseBytes(encDataBytes);
    encDataBytes = Arrays.copyOf(encDataBytes, 3); // we need only 3 bytes

    byte[] data = decryptTypePreserving(encDataBytes, 20);

    SDEUtils.reverseBytes(data);

    byte[] longData = new byte[8];
    System.arraycopy(data, 0, longData, 5, 3);
    ByteBuffer wrapped = ByteBuffer.wrap(longData);

    long output = wrapped.getLong();

    return daysToDate(output);
  }


  static int timeToSeconds(java.sql.Time time)
  {
    LocalTime local = time.toLocalTime();
    return (local.getHour() * 60 + local.getMinute()) * 60 + local.getSecond();
  }

  static java.sql.Time secondstoTime(int seconds)
  {
    int hours = seconds / 3600;  seconds %= 3600;
    int minutes = seconds / 60;  seconds %= 60;

    LocalTime local = LocalTime.of(hours, minutes, seconds);
    return java.sql.Time.valueOf(local);
  }

  /**
   * Encrypt a Time value
   *
   * @param time The time to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.sql.Time encryptTypePreserving(java.sql.Time time) throws SecurityException
  {
    int seconds = timeToSeconds(time);
    byte[] in = new byte[3];

    for (;;)
    {
      in[0] = (byte)(seconds & 0xff);
      in[1] = (byte)((seconds>>8) & 0xff);
      in[2] = (byte)((seconds>>16) & 0x03);

      byte[] out = encryptTypePreserving(in, 18);
      if (out==null) throw new RuntimeException("encryptTime failed");

      seconds =
        (out[0] & 0xff) |
        ((out[1] & 0xff)<<8) |
        ((out[2] & 0x3)<<16);

      if (seconds<86400) break;
    }
    return secondstoTime(seconds);
  }

  /**
   * Decrypt an encryptyed Time value
   *
   * @param time The encrypted value
   * @return Time value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Time decryptTypePreserving(java.sql.Time time) throws SecurityException
  {
    int seconds = timeToSeconds(time);
    byte[] in = new byte[3];

    for (;;)
    {
      in[0] = (byte)(seconds & 0xff);
      in[1] = (byte)((seconds>>8) & 0xff);
      in[2] = (byte)((seconds>>16) & 0x03);

      byte[] out = decryptTypePreserving(in, 18);
      if (out==null) throw new RuntimeException("decryptTime failed");

      seconds =
        (out[0] & 0xff) |
        ((out[1] & 0xff)<<8) |
        ((out[2] & 0x3)<<16);

      if (seconds<86400) break;
    }
    return secondstoTime(seconds);
  }

  /**
   * Encrypt a Timestamp value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.sql.Timestamp encryptTypePreserving(java.sql.Timestamp data) throws SecurityException
  {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(data);

    Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

    utcCal.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
    utcCal.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
    utcCal.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
    utcCal.set(Calendar.MILLISECOND, calendar.get(Calendar.MILLISECOND));
    utcCal.set(Calendar.SECOND, calendar.get(Calendar.SECOND));
    utcCal.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE));
    utcCal.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));

    long dateAsLongUTC = utcCal.getTimeInMillis();

    byte[] dataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(dateAsLongUTC).array();


    SDEUtils.reverseBytes(dataBytes);
    dataBytes = Arrays.copyOf(dataBytes, 6);

    byte[] encData = encryptTypePreserving(dataBytes, 46);

    SDEUtils.reverseBytes(encData);
    byte[] longData = new byte[8];
    System.arraycopy(encData, 0, longData, 2, 6);
    ByteBuffer wrapped = ByteBuffer.wrap(longData);

    long output = wrapped.getLong();
    java.sql.Timestamp date = new java.sql.Timestamp(output);

    return date;
  }

  /**
   * Decrypt an encryptyed Timestamp value
   *
   * @param encData The encrypted value
   * @return Timestamp value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Timestamp decryptTypePreserving(java.sql.Timestamp encData) throws SecurityException
  {
    long dateAsLong = encData.getTime();
    byte[] encDataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(dateAsLong).array();

    SDEUtils.reverseBytes(encDataBytes);
    encDataBytes = Arrays.copyOf(encDataBytes, 6);

    byte[] data = decryptTypePreserving(encDataBytes, 46);

    SDEUtils.reverseBytes(data);
    byte[] longData = new byte[8];
    System.arraycopy(data, 0, longData, 2, 6);

    ByteBuffer wrapped = ByteBuffer.wrap(longData);

    long output = wrapped.getLong();

    Calendar localCal = Calendar.getInstance();
    Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

    utcCal.setTimeInMillis(output);
    localCal.setTimeInMillis(0); // Epoch
    localCal.set(Calendar.MILLISECOND, utcCal.get(Calendar.MILLISECOND));
    localCal.set(Calendar.SECOND, utcCal.get(Calendar.SECOND));
    localCal.set(Calendar.MINUTE, utcCal.get(Calendar.MINUTE));
    localCal.set(Calendar.HOUR_OF_DAY, utcCal.get(Calendar.HOUR_OF_DAY));
    localCal.set(Calendar.DAY_OF_MONTH, utcCal.get(Calendar.DAY_OF_MONTH));
    localCal.set(Calendar.MONTH, utcCal.get(Calendar.MONTH));
    localCal.set(Calendar.YEAR, utcCal.get(Calendar.YEAR));

    java.sql.Timestamp date = new java.sql.Timestamp(localCal.getTimeInMillis());
    return date;
  }

  /**
   * Encrypt a Timestamp value in order preserving form, encrypted value is of string type
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public String encryptOrderPreserving(java.sql.Timestamp data) throws SecurityException
  {
    long time = data.getTime();
    int nanos = data.getNanos();

    ByteBuffer encDataBytesBuffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE + Integer.SIZE / Byte.SIZE);

    encDataBytesBuffer.putLong(0, time);
    encDataBytesBuffer.putInt(Long.SIZE / Byte.SIZE, nanos);

    byte[] dataBytes = encDataBytesBuffer.array();
    byte[] encData = encryptOPE(dataBytes);

    return SDEUtils.bytesToStringOP(encData);
  }

  /**
   * Decrypt a Timestamp value encrypted with order preserving encryption
   *
   * @param encDataStr The encrypted value
   * @return Timestamp value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Timestamp decryptOrderPreservingTS(String encDataStr) throws SecurityException
  {
    byte[] encData = SDEUtils.stringToBytesOP(encDataStr);

    byte[] data = decryptOPE(encData);

    ByteBuffer wrapped = ByteBuffer.wrap(data); // big-endian by default

    long time = wrapped.getLong(0);
    int nanos = wrapped.getInt(Long.SIZE / Byte.SIZE);
    java.sql.Timestamp ts = new java.sql.Timestamp(time);
    ts.setNanos(nanos);

    return ts;
  }

  /**
   * Encrypt a boolean value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public boolean encryptTypePreserving(boolean data) throws SecurityException
  {
    byte[] in = new byte[1];
    in[0] = data ? (byte) 1 : (byte) 0;
    return encryptTypePreserving(in, 1)[0] != 0;
  }

  /**
   * Decrypt an encryptyed boolean value
   *
   * @param encData The encrypted value
   * @return Boolean value in plain
   * @throws SecurityException In case of decryption error
   */
  public boolean decryptTypePreserving(boolean encData) throws SecurityException
  {
    byte[] in = new byte[1];
    in[0] = encData ? (byte) 1 : (byte) 0;
    return decryptTypePreserving(in, 1)[0] != 0;
  }

  /**
   * Encrypt a Blob value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.sql.Blob encryptTypePreserving(java.sql.Blob data) throws SecurityException
  {
    try
    {
      byte[] enc = encryptTypePreserving(data.getBytes(1, (int) data.length()));
      return new javax.sql.rowset.serial.SerialBlob(enc);
    }
    catch (SQLException e)
    {
      throw new SecurityException(e);
    }
  }

  /**
   * Decrypt an encryptyed Blob value
   *
   * @param encData The encrypted value
   * @return Blob value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Blob decryptTypePreserving(java.sql.Blob encData) throws SecurityException
  {
    try
    {
      byte[] data = decryptTypePreserving(encData.getBytes(1, (int) encData.length()));
      return new javax.sql.rowset.serial.SerialBlob(data);
    }
    catch (SQLException e)
    {
      throw new SecurityException(e);
    }
  }

  /**
   * Encrypt a Clob value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.sql.Clob encryptTypePreserving(java.sql.Clob data) throws SecurityException
  {
    try
    {
      String enc = encryptTypePreserving(data.getSubString(1, (int) data.length()), true);
      return new javax.sql.rowset.serial.SerialClob(enc.toCharArray());
    }
    catch (SQLException e)
    {
      throw new SecurityException(e);
    }
  }

  /**
   * Decrypt an encryptyed Clob value
   *
   * @param encData The encrypted value
   * @return Clob value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.sql.Clob decryptTypePreserving(java.sql.Clob encData) throws SecurityException
  {
    try
    {
      String data = decryptTypePreserving(encData.getSubString(1, (int) encData.length()), true);
      return new javax.sql.rowset.serial.SerialClob(data.toCharArray());
    }
    catch (SQLException e)
    {
      throw new SecurityException(e);
    }
  }

  private static boolean lsb(int x)
  {
    return (x & 1) != 0;
  }

  private static boolean xorAllBits(byte[] v)
  {
    byte x = 0;
    for (int i=0; i> i);
    return res;
  }

  /**
   * Encrypt a BigDecimal value
   *
   * @param data The data to encrypt
   * @return Encrypted value
   * @throws SecurityException In case of encryption error
   */
  public java.math.BigDecimal encryptTypePreserving(java.math.BigDecimal data) throws SecurityException
  {
    try
    {
      int scale = data.scale();
      BigInteger bi = data.unscaledValue();
      boolean bNegative = bi.signum() == -1;
      bi = bi.abs();
      byte[] biBytes = bi.toByteArray();

      int biBytesLength = biBytes.length;
      if (biBytesLength > 12)
      {
        if ((biBytesLength == 13) && (biBytes[0] == 0))
        {
          // ok, ignore that
          byte[] newBytes = new byte[12];
          System.arraycopy(biBytes, 1, newBytes, 0, 12);
          biBytes = newBytes;
        }
        else
        {
          throw new IllegalArgumentException("Value encryption is not supported");
        }
      }
      else
      {
        biBytes = SDEUtils.addLeading(biBytes, (byte) 0, 12 - biBytesLength);
      }
      boolean sig = bNegative ^ xorAllBits(biBytes);
      byte[] enc = encryptTypePreserving(biBytes);
      BigInteger encBi = new BigInteger(sig ? 1 : -1, enc);

      return new java.math.BigDecimal(encBi, scale);
    }
    catch (Exception e)
    {
      throw new SecurityException(e);
    }
  }

  /**
   * Decrypt an encryptyed BigDecimal value
   *
   * @param encData The encrypted value
   * @return BigDecimal value in plain
   * @throws SecurityException In case of decryption error
   */
  public java.math.BigDecimal decryptTypePreserving(java.math.BigDecimal encData) throws SecurityException
  {
    try
    {
      int scale = encData.scale();
      BigInteger bi = encData.unscaledValue();
      boolean bNegative = bi.signum() == -1;
      bi = bi.abs();

      byte[] biBytes = bi.toByteArray();

      int biBytesLength = biBytes.length;
      if (biBytesLength > 12)
      {
        if ((biBytesLength == 13) && (biBytes[0] == 0))
        {
          // ok, ignore that
          byte[] newBytes = new byte[12];
          System.arraycopy(biBytes, 1, newBytes, 0, 12);
          biBytes = newBytes;
        }
        else
        {
          throw new IllegalArgumentException("Value encryption is not supported");
        }
      }
      else
      {
        biBytes = SDEUtils.addLeading(biBytes, (byte) 0, 12 - biBytesLength);
      }

      byte[] data = decryptTypePreserving(biBytes);
      boolean sig = bNegative ^ xorAllBits(data);

      data = SDEUtils.addLeading(data, (byte) 0, 1);  // make it positive for sure
      BigInteger dataBi = new BigInteger(sig ? 1 : -1, data);
      return new java.math.BigDecimal(dataBi, scale);
    }
    catch (Exception e)
    {
      throw new SecurityException(e);
    }
  }

  /**
   * Encrypt an email address in format preserving form, where the encrypted value is also a legitimate email address. 
* It can contain these characters: A-Z, a-z, 0-9, .!#$%&*+-/={|}~(),:;<>[]
* It must contain an @ as a separator and cannot contain spaces. * * @param in Email address to encrypt * @param maxLen Maximum size of all email addresses encrypted * @return The encrypted value * @throws SecurityException In case of encryption error */ public String encryptEMailAddress(String in, int maxLen) throws SecurityException { if (purpose != SDEKey.PURPOSE_EMAIL_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.EMail.encrypt(rawKey, in, maxLen); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, true); oper.fpeMaxLen = maxLen; oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_EMAIL; byte[] outBuf = oper.enc(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Decrypt an encrypted email address, where the encrypted value is also an email address. * * @param in The encrypted value * @return Original plain email address * @throws SecurityException In case of decryption error */ public String decryptEMailAddress(String in) throws SecurityException { if (purpose != SDEKey.PURPOSE_EMAIL_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.EMail.decrypt(rawKey, in); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, false); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_EMAIL; byte[] outBuf = oper.dec(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Encrypt a credit card number in format preserving form, where the encrypted value is also a legitimate * credit card number. Encryption uses a Luhn algorithm to verify valid credit card numbers. The number is * * between 12 and 19 digits. Any non-digits, such as hyphens, are passed as-is. * * @param in Credit card number to encrypt * @return The encrypted value * @throws SecurityException In case of encryption error */ public String encryptCreditCard(String in) throws SecurityException { return encryptCreditCard(in, null); } /** * Encrypt a credit card number in format preserving form, where the encrypted value is also a legitimate * credit card number. Encryption uses a Luhn algorithm to verify valid credit card numbers. The number is * * between 12 and 19 digits. Any non-digits, such as hyphens, are passed as-is. * * @param in Credit card number to encrypt * @param format The credit card number, where the # characters get encrypted, the ? characters are passed as plain text, and other characters are passed as-is to the output. For example, for "????-####-####-####", the first 4 numbers are plain and the rest of the credit card number is encrypted. * @return The encrypted value * @throws SecurityException In case of encryption error */ public String encryptCreditCard(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_CREDIT_CARD_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.CreditCard.encrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, true); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_CREDIT_CARD; oper.fpeFormat = format; byte[] outBuf = oper.enc(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Decrypt a credit card number, where the encrypted value is also a credit card number * * @param in The encrypted value * @return Original plain credit card * @throws SecurityException In case of decryption error */ public String decryptCreditCard(String in) throws SecurityException { return decryptCreditCard(in, null); } /** * Decrypt a credit card number, where the encrypted value is also a credit card number * * @param in The encrypted value * @param format The credit card number, where the # characters get decrypted, the ? characters are passed as plain text, and other characters are passed as-is to the output. For example, for "????-####-####-####", the first 4 numbers are plain and the rest of the credit card number is decrypted. * @return Original plain credit card * @throws SecurityException In case of decryption error */ public String decryptCreditCard(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_CREDIT_CARD_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.CreditCard.decrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, false); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_CREDIT_CARD; oper.fpeFormat = format; byte[] outBuf = oper.dec(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Encrypt a US phone in format preserving form, where the encrypted value is also a legitimate US phone number.
* The number is 10 digits. Any non-digits, such as hyphens, are passed as-is. It is validated by checking the * format aaa-bcc-dddd, where aaa > 200, b >= 2, and cc is not 11. * * @param in Phone number to encrypt * @param format The output format, where the # characters get encrypted and other characters are passed as-is to the output. For example, the format could be "###-###-####". * @return The encrypted value * @throws SecurityException In case of encryption error */ public String encryptUSPhone(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_US_PHONE_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.USPhone.encrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, true); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_US_PHONE; oper.fpeFormat = format; byte[] outBuf = oper.enc(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Decrypt an encrypted US phone number, where the encrypted value is also a US phone number * * @param in The encrypted value * @param format The output format, where the # characters get decrypted and other characters are passed as-is to the output. For example, the format could be "###-###-####". * @return Original plain US phone number * @throws SecurityException In case of decryption error */ public String decryptUSPhone(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_US_PHONE_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.USPhone.decrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, false); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_US_PHONE; oper.fpeFormat = format; byte[] outBuf = oper.dec(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Encrypt an SSN in format preserving form, where the encrypted value is also a legitimate SSN.

* The SSN is a 9 digit number. Any non-digits, such as hyphens, are passed as-is. The SSN can be all * numbers except:
* - Numbers with all zeros in any digit group (000-##-####, ###-00-####, ###-##-0000).
* - Numbers with 666 or 900-999 (Individual Taxpayer Identification Number) in the first digit group. * * @param in SSN to encrypt * @param format The output format, where the # characters get encrypted, the ? characters are passed as plain text, and other characters are passed as-is to the output. Note that only the last 4 numbers can be plain text, so only "###-##-####" and "###-##-????" are acceptable formats (with or without the hyphen delimiters). * @return The encrypted value * @throws SecurityException In case of encryption error */ public String encryptSSN(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_SSN_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.SSN.encrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, true); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_SSN; oper.fpeFormat = format; byte[] outBuf = oper.enc(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } /** * Decrypt an encrypted SSN, where the encrypted value is also SSN * * @param in The encrypted value * @param format The output format, where the # characters get decrypted, the ? characters are passed as plain text, and other characters are passed as-is to the output. Note that only the last 4 numbers can be plain text, so only "###-##-####" and "###-##-????" are acceptable formats (with or without the hyphen delimiters). * @return Original plain SSN * @throws SecurityException In case of decryption error */ public String decryptSSN(String in, String format) throws SecurityException { if (purpose != SDEKey.PURPOSE_SSN_ENC) throw new IllegalArgumentException("Invalid purpose"); if (rawKey!=null) { return FPE.SSN.decrypt(rawKey, in, format); } byte[] inBuf = in.getBytes(StandardCharsets.UTF_8); CipherOper oper = newOperation(CipherMode.FPE, false); oper.fpeMode = DYCK_FPE_PARAMS.DYCK_FPE_SSN; oper.fpeFormat = format; byte[] outBuf = oper.dec(inBuf); return new String(outBuf, StandardCharsets.UTF_8); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy