-1

I decided to use generic object pooling to reuse cipher object by following A Generic and Concurrent Object Pool . The different is that the article using Connection, but I'm using Cipher.

Eracom

Pool<Cipher> pool = new BoundedBlockingPool<Cipher>(5, new CipherPickerValidator(), new CipherPicker("xxxx"));  

     public Key unwrapKey(byte[] tmkByte) throws Exception {
            Cipher cipher = pool.get();
            System.out.println("Cipher :" + cipher);
            try{
            cipher.init(Cipher.DEPT_MODE, mkkey, alSpec);
            }catch(Exception e)
            {
                System.out.println(e);

            }
            byte[] de = cipher.doFinal(tmkByte);
            SecretKey tmk = new SecretKeySpec(de, "De");
            return tmk;
        }

BoundedBlockingPool

public BoundedBlockingPool(
        int size,
        Validator<T> validator,
        ObjectFactory<T> objectFactory) {
    super();

    this.objectFactory = objectFactory;
    this.size = size;
    this.validator = validator;

    objects = new LinkedBlockingQueue<T>(size);

    initializeObjects();

    shutdownCalled = false;
}

@Override
public T get(long timeOut, TimeUnit unit) {
    if (!shutdownCalled) {
        T t = null;

        try {
            t = objects.poll(timeOut, unit);

            return t;
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }

        return t;
    }

    throw new IllegalStateException(
            "Object pool is already shutdown");
}

@Override
public T get() {
    if (!shutdownCalled) {
        T t = null;

        try {
            t = objects.take();
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }

        return t;
    }

    throw new IllegalStateException(
            "Object pool is already shutdown");
}

@Override
public void shutdown() {
    shutdownCalled = true;

    executor.shutdownNow();

    clearResources();
}

private void clearResources() {
    objects.stream().forEach((t) -> {
        validator.invalidate(t);
    });
}

@Override
protected void returnToPool(T t) {
    if (validator.isValid(t)) {
        executor.submit(new ObjectReturner(objects, t));
    }
}

@Override
protected void handleInvalidReturn(T t) {

}

@Override
protected boolean isValid(T t) {
    return validator.isValid(t);
}

private void initializeObjects() {
    for (int i = 0; i < size; i++) {
        objects.add(objectFactory.createNew());
    }
}

private class ObjectReturner<E>
        implements Callable<Void> {
    private final BlockingQueue<E> queue;
    private E e;

    public ObjectReturner(BlockingQueue<E> queue, E e) {
        this.queue = queue;
        this.e = e;
    }

    @Override
    public Void call() {
        while (true) {
            try {
                queue.put(e);
                break;
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }

        return null;
    }
}

CipherPicker

public CipherPicker(String instances) {
    super();
    this.instance = instances;
}

@Override
public Cipher createNew() {
    try {
        System.out.print("Instances : " + instance);
        return Cipher.getInstance(this.instance);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException se) {
        throw new IllegalArgumentException(
                "Unable to create new cipher", se);
    }
}

Error

Cipher :javax.crypto.Cipher@45c74f8
<log realm="GenerateIPEK" at="Thu Apr 20 18:20:27.737 MYT 2017">
  <error>
    <exception name="Cipher not initialized">
    java.lang.IllegalStateException: Cipher not initialized
    at javax.crypto.Cipher.checkCipherState(Cipher.java:1750)
    at javax.crypto.Cipher.doFinal(Cipher.java:2157)
    at com.rh.host.EracomJCE.unwrapKey(EracomJCE.java:65)
    at com.rh.host.tm.GenerateIPEK.doPrepare(GenerateIPEK.java:70)
    at com.rh.host.tm.TxnSupport.prepare(TxnSupport.java:36)
    at org.jpos.transaction.TransactionManager.prepare(TransactionManager.java:473)
    at org.jpos.transaction.TransactionManager.prepare(TransactionManager.java:526)
    at org.jpos.transaction.TransactionManager.run(TransactionManager.java:257)
    at java.lang.Thread.run(Thread.java:745)
    </exception>
  </error>

Can someone tell me what are the correct way to do this ? Been stucked for several days. Am I on the correct path ??? Big thanks !

Edit

Assuming I have two methods and will be called after this. So I should write like this ?

    public Key unwrapKey(byte[] tmkByte) throws Exception {
                Cipher cipher = pool.get();
                byte[] de = cipher.doFinal(tmkByte);
                SecretKey tmk = new SecretKeySpec(de, "De");
                return tmk;    
            }

   public Key wrapKey(byte[] tByte) throws Exception {
                Cipher cipher = pool.get();  
                // byte,SecretKey...  
            }
Tony
  • 2,515
  • 14
  • 38
  • 71

1 Answers1

3

I think you're on the correct path. I think you need to initialize the Cipher in the createNew() method:

@Override
public Cipher createNew() {
    try {
        System.out.print("Instances : " + instance);
        Cipther result = Cipher.getInstance(this.instance);
        result.init(...); // use the right init params, e.g. opmode, crypto key
        return result;
    } catch (NoSuchAlgorithmException | NoSuchPaddingException se) {
        throw new IllegalArgumentException(
                "Unable to create new cipher", se);
    }
}
Tamas Rev
  • 7,008
  • 5
  • 32
  • 49
  • Hey, can you check my post again ? Thanks – Tony Apr 20 '17 at 16:32
  • I checked. You are asking questions about how to use Cipher. I think [this Q&A](http://stackoverflow.com/q/15554296/187808) explains it well. – Tamas Rev Apr 20 '17 at 16:46
  • Currently I have many methods which will create Cipher. I want to use Generic Object Pool so the object can be reused. – Tony Apr 21 '17 at 07:29
  • Let's just solve one problem at a time. **First problem:** creating Ciphers without caching, pooling, whatever. Make sure it works. **Second problem:** build the caching around this Cipher. – Tamas Rev Apr 21 '17 at 09:03
  • If I replace `Cipher cipher = pool.get();` with `Cipher cipher = cipher.getInstances("xxx");`, it works perfectly. – Tony Apr 21 '17 at 09:08
  • what should I do next sir ? Thanks – Tony Apr 21 '17 at 09:37
  • I took another look at Cipher code and this pool. It occurred to me that there is an `init()` call within the `unwrap` method. According to the Cipher docs: *Note that when a Cipher object is initialized, it loses all previously-acquired state. In other words, initializing a Cipher is equivalent to creating a new instance of that Cipher and initializing it.*. So I guess you need to decide the lifecycle of the Cipher object. I'd prefer only 1 init per Cipher. However, it depends on the *client* of this pool too. I.e. how do you want to use it? – Tamas Rev Apr 21 '17 at 10:53
  • It depends on why you need that generic object pool. I.e. is creating objects really a performance bottleneck? Do you have lots of pools so you need a generic solution? Can you use a 3rd party pool, e.g. one from Spring Framework? Are you actually writing a framework? :) – Tamas Rev Apr 24 '17 at 10:26