private int implDoFinal(byte[] out, int outOfs, int outLen)
     throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
   int requiredOutLen = doFinalLength(0);
   if (outLen < requiredOutLen) {
     throw new ShortBufferException();
   }
   try {
     ensureInitialized();
     int k = 0;
     if (encrypt) {
       if (paddingObj != null) {
         int actualPadLen = paddingObj.setPaddingBytes(padBuffer, requiredOutLen - bytesBuffered);
         k =
             token.p11.C_EncryptUpdate(
                 session.id(), 0, padBuffer, 0, actualPadLen, 0, out, outOfs, outLen);
       }
       k += token.p11.C_EncryptFinal(session.id(), 0, out, (outOfs + k), (outLen - k));
     } else {
       if (paddingObj != null) {
         if (padBufferLen != 0) {
           k =
               token.p11.C_DecryptUpdate(
                   session.id(), 0, padBuffer, 0, padBufferLen, 0, padBuffer, 0, padBuffer.length);
         }
         k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k);
         int actualPadLen = paddingObj.unpad(padBuffer, k);
         k -= actualPadLen;
         System.arraycopy(padBuffer, 0, out, outOfs, k);
       } else {
         k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs, outLen);
       }
     }
     return k;
   } catch (PKCS11Exception e) {
     handleException(e);
     throw new ProviderException("doFinal() failed", e);
   } finally {
     reset();
   }
 }
  private int implDoFinal(ByteBuffer outBuffer)
      throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
    int outLen = outBuffer.remaining();
    int requiredOutLen = doFinalLength(0);
    if (outLen < requiredOutLen) {
      throw new ShortBufferException();
    }

    try {
      ensureInitialized();

      long outAddr = 0;
      byte[] outArray = null;
      int outOfs = 0;
      if (outBuffer instanceof DirectBuffer) {
        outAddr = ((DirectBuffer) outBuffer).address();
        outOfs = outBuffer.position();
      } else {
        if (outBuffer.hasArray()) {
          outArray = outBuffer.array();
          outOfs = outBuffer.position() + outBuffer.arrayOffset();
        } else {
          outArray = new byte[outLen];
        }
      }

      int k = 0;

      if (encrypt) {
        if (paddingObj != null) {
          int actualPadLen = paddingObj.setPaddingBytes(padBuffer, requiredOutLen - bytesBuffered);
          k =
              token.p11.C_EncryptUpdate(
                  session.id(), 0, padBuffer, 0, actualPadLen, outAddr, outArray, outOfs, outLen);
        }
        k += token.p11.C_EncryptFinal(session.id(), outAddr, outArray, (outOfs + k), (outLen - k));
      } else {
        if (paddingObj != null) {
          if (padBufferLen != 0) {
            k =
                token.p11.C_DecryptUpdate(
                    session.id(), 0, padBuffer, 0, padBufferLen, 0, padBuffer, 0, padBuffer.length);
            padBufferLen = 0;
          }
          k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k);
          int actualPadLen = paddingObj.unpad(padBuffer, k);
          k -= actualPadLen;
          outArray = padBuffer;
          outOfs = 0;
        } else {
          k = token.p11.C_DecryptFinal(session.id(), outAddr, outArray, outOfs, outLen);
        }
      }
      if ((!encrypt && paddingObj != null)
          || (!(outBuffer instanceof DirectBuffer) && !outBuffer.hasArray())) {
        outBuffer.put(outArray, outOfs, k);
      } else {
        outBuffer.position(outBuffer.position() + k);
      }
      return k;
    } catch (PKCS11Exception e) {
      handleException(e);
      throw new ProviderException("doFinal() failed", e);
    } finally {
      reset();
    }
  }