/*********************************************************** * $Id: PKCS11CipherSpi.java 33 2007-01-26 19:37:44Z wolfgang.glas $ * * PKCS11 provider of the OpenSC project http://www.opensc-project.org * * Copyright (C) 2002-2006 ev-i Informationstechnologie GmbH * * Created: Jul 23, 2006 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ***********************************************************/ package org.opensc.pkcs11.spi; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherSpi; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import org.opensc.pkcs11.PKCS11Provider; import org.opensc.pkcs11.wrap.PKCS11Exception; import org.opensc.pkcs11.wrap.PKCS11Mechanism; import org.opensc.pkcs11.wrap.PKCS11PrivateKey; import org.opensc.pkcs11.wrap.PKCS11PublicKey; import org.opensc.pkcs11.wrap.PKCS11SessionChild; import de.kp.logging.Log; import de.kp.logging.LogFactory; /** * This class is the implementation of the cryptographic Cipher service * for the OpenSC PKCS#11 provider. * * @author wglas * @author Stefan Krusche (krusche@dr-kruscheundpartner.de) * */ public class PKCS11CipherSpi extends CipherSpi { static Log log = LogFactory.getLog(PKCS11CipherSpi.class); PKCS11Provider provider; String algorithm; PKCS11SessionChild worker; PrivateKey privateKey; PublicKey publicKey; int mode; long count; /** * Contructs an instance of PKCS11CipherSpi using the given provider * and algorithm. Usually, you will not have to call this contructor, * This class is implicitly instantiated using <tt>Cipher.getInstance</tt> * * @see javax.crypto.Cipher#getInstance(java.lang.String, java.security.Provider) * */ public PKCS11CipherSpi(PKCS11Provider provider, String algorithm) { super(); this.provider = provider; this.algorithm = algorithm; this.mode = 0; this.count = 0; } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineSetMode(java.lang.String) */ @Override protected void engineSetMode(String engineMode) throws NoSuchAlgorithmException { if (!engineMode.equals("ECB")) throw new NoSuchAlgorithmException("Only ECB mode is supported."); } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineSetPadding(java.lang.String) */ @Override protected void engineSetPadding(String padding) throws NoSuchPaddingException { if (!padding.equals("PKCS1Padding")) throw new NoSuchPaddingException("Only PKCS1Padding is supported."); } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineGetKeySize(java.security.Key) */ @Override protected int engineGetKeySize(Key key) throws InvalidKeyException { if (key instanceof PKCS11PrivateKey) { return ((PKCS11PrivateKey)key).getKeyBits(); } if (key instanceof PKCS11PublicKey) { return ((PKCS11PublicKey)key).getKeyBits(); } throw new InvalidKeyException("Invalid key class "+key.getClass()); } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineGetBlockSize() */ @Override protected int engineGetBlockSize() { if (this.privateKey != null) { return ((PKCS11PrivateKey)this.privateKey).getKeyBits() / 8; } if (this.publicKey != null && this.publicKey instanceof PKCS11PublicKey) { return ((PKCS11PublicKey)this.publicKey).getKeyBits() / 8; } return 1; } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineGetOutputSize(int) */ @Override protected int engineGetOutputSize(int sz) { int block_sz = engineGetBlockSize(); return (sz + block_sz - 1) / block_sz; } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineGetIV() */ @Override protected byte[] engineGetIV() { return null; } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineGetParameters() */ @Override protected AlgorithmParameters engineGetParameters() { return null; } private int getPKCS11MechanismType() throws InvalidKeyException { int pkcs11_alg; if (this.algorithm.equals("RSA/ECB/PKCS1Padding")) pkcs11_alg = PKCS11Mechanism.CKM_RSA_PKCS; else throw new InvalidKeyException("Signature algorithm ["+ this.algorithm+"] is unsupported."); return pkcs11_alg; } private native void initEncryptNative(long pvh, long shandle, long hsession, long handle, int pkcs11_alg) throws PKCS11Exception; private native void initDecryptNative(long pvh, long shandle, long hsession, long handle, int pkcs11_alg) throws PKCS11Exception; /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineInit(int, java.security.Key, java.security.SecureRandom) */ @Override protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { if (opmode == Cipher.ENCRYPT_MODE) { if (! (key instanceof PKCS11SessionChild)) throw new InvalidKeyException("PKCS11 signature engine expects a valid PKCS11 object."); if (!this.algorithm.startsWith(key.getAlgorithm())) throw new InvalidKeyException("PKCS11 key algorithm ["+ key.getAlgorithm()+ "] is incompatible with signature algorithm ["+ this.algorithm+"]."); int pkcs11_alg = getPKCS11MechanismType(); this.worker = (PKCS11SessionChild)key; if (key instanceof PublicKey) { this.publicKey = (PublicKey)key; this.privateKey = null; } else if (key instanceof PrivateKey) { this.publicKey = null; this.privateKey = (PrivateKey)key; } else throw new InvalidKeyException("PKCS11 signature engine expects a public or private key for encryption mode."); this.mode = opmode; try { initEncryptNative(this.worker.getPvh(), this.worker.getSlotHandle(),this.worker.getSessionHandle(), this.worker.getHandle(),pkcs11_alg); } catch (PKCS11Exception e) { throw new InvalidKeyException("PKCS11 exception initializing encryption:",e); } } else if (opmode == Cipher.DECRYPT_MODE) { if (! (key instanceof PKCS11SessionChild)) throw new InvalidKeyException("PKCS11 signature engine expects a valid PKCS11 object."); if (!this.algorithm.startsWith(key.getAlgorithm())) throw new InvalidKeyException("PKCS11 key algorithm ["+ key.getAlgorithm()+ "] is incompatible with signature algorithm ["+ this.algorithm+"]."); int pkcs11_alg = getPKCS11MechanismType(); this.worker = (PKCS11SessionChild)key; if (key instanceof PublicKey) { this.publicKey = (PublicKey)key; this.privateKey = null; } else if (key instanceof PrivateKey) { this.publicKey = null; this.privateKey = (PrivateKey)key; } else throw new InvalidKeyException("PKCS11 signature engine expects a public or private key for decryption mode."); this.mode = opmode; try { initDecryptNative(this.worker.getPvh(), this.worker.getSlotHandle(),this.worker.getSessionHandle(), this.worker.getHandle(),pkcs11_alg); } catch (PKCS11Exception e) { throw new InvalidKeyException("PKCS11 exception initializing decryption:",e); } } else throw new InvalidKeyException("Invalid operation mode ["+opmode+"] in PKCS11CipherSpi.engineInit()."); this.count = 0; } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineInit(int, java.security.Key, java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) */ @Override protected void engineInit(int opmode, Key key, AlgorithmParameterSpec param, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { engineInit(opmode,key,random); } /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineInit(int, java.security.Key, java.security.AlgorithmParameters, java.security.SecureRandom) */ @Override protected void engineInit(int opmode, Key key, AlgorithmParameters param, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { engineInit(opmode,key,random); } private native byte[] updateDecryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; private native byte[] updateEncryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineUpdate(byte[], int, int) */ @Override protected byte[] engineUpdate(byte[] data, int off, int len) { try { this.count += len; if (this.mode == Cipher.DECRYPT_MODE) return updateDecryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),data,off,len); else return updateEncryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),data,off,len); } catch (PKCS11Exception e) { log.error("PKCS11Exception caught:",e); } return null; } private native int updateDecryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; private native int updateEncryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineUpdate(byte[], int, int, byte[], int) */ @Override protected int engineUpdate(byte[] input, int off, int len, byte[] output, int output_off) throws ShortBufferException { try { this.count += len; if (this.mode == Cipher.DECRYPT_MODE) return updateDecryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); else return updateEncryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); } catch (PKCS11Exception e) { log.error("PKCS11Exception caught:",e); throw new ShortBufferException("PKCS11 exception:"+e); } } private native byte[] doFinalDecryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; private native byte[] doFinalEncryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; private native byte[] doDecryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; private native byte[] doEncryptNative(long pvh, long shandle, long hsession, long handle, byte[] data, int off, int len) throws PKCS11Exception; /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineDoFinal(byte[], int, int) */ @Override protected byte[] engineDoFinal(byte[] input, int off, int len) throws IllegalBlockSizeException, BadPaddingException { byte[] ret; try { if (this.mode == Cipher.DECRYPT_MODE) if (this.count == 0) ret = doDecryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),input,off,len); else ret = doFinalDecryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),input,off,len); else if (this.count == 0) ret = doEncryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),input,off,len); else ret = doFinalEncryptNative(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(),input,off,len); } catch (PKCS11Exception e) { log.error("PKCS11Exception caught:",e); throw new IllegalBlockSizeException("PKCS11Exception caught:"+e); } this.count = 0; return ret; } private native int doFinalDecryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; private native int doFinalEncryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; private native int doDecryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; private native int doEncryptNativeOff(long pvh, long shandle, long hsession, long handle, byte[] input, int off, int len, byte[] output, int ouput_off) throws PKCS11Exception; /* (non-Javadoc) * @see javax.crypto.CipherSpi#engineDoFinal(byte[], int, int, byte[], int) */ @Override protected int engineDoFinal(byte[] input, int off, int len, byte[] output, int output_off) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { int ret; try { if (this.mode == Cipher.DECRYPT_MODE) if (this.count == 0) ret = doDecryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); else ret = doFinalDecryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); else if (this.count == 0) ret = doEncryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); else ret = doFinalEncryptNativeOff(this.worker.getPvh(),this.worker.getSlotHandle(), this.worker.getSessionHandle(),this.worker.getHandle(), input,off,len,output,output_off); } catch (PKCS11Exception e) { log.error("PKCS11Exception caught:",e); throw new ShortBufferException("PKCS11 exception:"+e); } this.count = 0; return ret; } }