/* * MyRSACryptoToken.java * * Copyright � 1998-2011 Research In Motion Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Note: For the sake of simplicity, this sample application may not leverage * resource bundles and resource strings. However, it is STRONGLY recommended * that application developers make use of the localization features available * within the BlackBerry development platform to ensure a seamless application * experience across a variety of languages and geographies. For more information * on localizing your application, please refer to the BlackBerry Java Development * Environment Development Guide associated with this release. */ package com.rim.samples.device.smartcarddriverdemo; import net.rim.device.api.crypto.CryptoSystem; import net.rim.device.api.crypto.CryptoTokenCancelException; import net.rim.device.api.crypto.CryptoTokenException; import net.rim.device.api.crypto.CryptoTokenPrivateKeyData; import net.rim.device.api.crypto.CryptoUnsupportedOperationException; import net.rim.device.api.crypto.RSACryptoSystem; import net.rim.device.api.crypto.RSACryptoToken; import net.rim.device.api.smartcard.SmartCardCancelException; import net.rim.device.api.smartcard.SmartCardException; import net.rim.device.api.smartcard.SmartCardFactory; import net.rim.device.api.smartcard.SmartCardID; import net.rim.device.api.smartcard.SmartCardRemovedException; import net.rim.device.api.smartcard.SmartCardSession; import net.rim.device.api.smartcard.SmartCardSessionClosedException; import net.rim.device.api.util.Persistable; /** * An implementation of an RSA cryptographic token. * * The RIM Crypto API will call this interface when a private key RSA operation * is to be performed. The operation should be delegated to the smart card. */ public final class MyRSACryptoToken extends RSACryptoToken implements Persistable { private static final String DECRYPT_DESC = "The private key will be used to decrypt encrypted data."; private static final String SIGN_DESC = "The private key will be used to generate a digital signature."; /** * Determine if this token does the user authentication for the system. If * not, the <code>KeyStore</code> will prompt for the key store password * when the user tries to access the private key. * * @return true if this token will prompt for the necessary user * authentication when private key access is requested. */ public boolean providesUserAuthentication() { return true; } /** * Indicates whether the chosen operation is supported by this CryptoToken * using the provided CryptoSytem. * <p> * * @param cryptoSystem * The CryptoSystem to check against. * @param operation * An integer, either KEY_GENERATION, PUBLIC_KEY_OPERATION, * PRIVATE_KEY_OPERATION, or some other value specific to the * cryptosystem that indicates the operation to be checked. */ public boolean isSupported(final CryptoSystem cryptoSystem, final int operation) { return operation == PRIVATE_KEY_OPERATION; } /** * Returns a boolean that determines if the given key and crypto system * support RSA encryption. * <p> * * @param cryptoSystem * The crypto system to check. * @param privateKeyData * The private key data. * @return A boolean that indicates if the token supports RSA encryption. * @throws CryptoTokenException * Thrown if an error occurs with a crypto token or the crypto * token is invalid. */ public boolean isSupportedDecryptRSA(final RSACryptoSystem cryptoSystem, final CryptoTokenPrivateKeyData privateKeyData) throws CryptoTokenException { return privateKeyData instanceof MyCryptoTokenData; } /** * Performs a raw RSA decryption operation. * <p> * Notes: The token should do a raw RSA private key operation on the input * data to reveal the plaintext bytes. The plaintext bytes will typically be * padded; the type of padding will depend on the application which * encrypted the data. Typically PKCS #1 version 2.0 will be used for * padding the data, but other schemes (such as OAEP) could be used. If the * token removes the padding, this method will need to re-add the same type * of padding before returning. Data encrypted with BlackBerry's S/MIME * implementation currently uses PKCS #1 padding but may use other padding * methods in the future. * <p> * * @param cryptoSystem * The crypto system associated with the token. * @param privateKeyData * The RSA private key. * @param input * The input data. * @param inputOffset * The offset in the input data to begin reading from. * @param output * The buffer for the output data. * @param outputOffset * The offset in the output buffer to begin writing at. * @throws CryptoTokenException * Thrown if an error occurs with a crypto token or the crypto * token is invalid. */ public void decryptRSA(final RSACryptoSystem cryptoSystem, final CryptoTokenPrivateKeyData privateKeyData, final byte[] input, final int inputOffset, final byte[] output, final int outputOffset) throws CryptoTokenException { try { signDecryptHelper(cryptoSystem, privateKeyData, input, inputOffset, output, outputOffset, DECRYPT_DESC, SmartCardSession.DECRYPT_OPERATION); } catch (final CryptoUnsupportedOperationException e) { throw new CryptoTokenException(e.toString()); } } /** * Performs a raw RSA signature operation. * <p> * Notes: The token should do a raw RSA private key operation on the input * data. The input data will typically be padded; the type of padding will * depend on the application requesting the signature. Typically PKCS #1 * version 2.0 will be used for padding the data, but other schemes (such as * PSS or ANSI X9.31) could be used. If the token requires that the padding * be removed before signing, this method will need to detect the type of * padding currently being used and remove it. The token should only * re-apply the same type of padding which was originally applied to the * data. If the token is unable to re-apply the same type of padding, a * CryptoUnsupportedOperationException should be thrown. Signature requests * which come from BlackBerry's S/MIME implementation currently use PKCS #1 * padding but may use other padding methods in the future. * <p> * * @param cryptoSystem * The crypto system associated with the token. * @param privateKeyData * The RSA private key. * @param input * The input data. * @param inputOffset * The offset in the input data to begin reading from. * @param output * The buffer for the output data. * @param outputOffset * The offset in the output buffer to begin writing at. * @throws CryptoTokenException * Thrown if an error occurs with the crypto token or the crypto * token is invalid. * @throws CryptoUnsupportedOperationException * Thrown if a call is made to an unsupported operation or if * the token does not support signing due to the type of padding * around the encoded message. */ public void signRSA(final RSACryptoSystem cryptoSystem, final CryptoTokenPrivateKeyData privateKeyData, final byte[] input, final int inputOffset, final byte[] output, final int outputOffset) throws CryptoTokenException, CryptoUnsupportedOperationException { signDecryptHelper(cryptoSystem, privateKeyData, input, inputOffset, output, outputOffset, SIGN_DESC, SmartCardSession.SIGN_OPERATION); } /** * A helper method for signing and decrypting since the operations are very * similar. */ private void signDecryptHelper(final RSACryptoSystem cryptoSystem, final CryptoTokenPrivateKeyData privateKeyData, final byte[] input, final int inputOffset, final byte[] output, final int outputOffset, final String accessReason, final int operation) throws CryptoTokenException, CryptoUnsupportedOperationException { SmartCardSession smartCardSession = null; try { if (privateKeyData instanceof MyCryptoTokenData) { final SmartCardID smartCardID = ((MyCryptoTokenData) privateKeyData).getSmartCardID(); smartCardSession = SmartCardFactory.getSmartCardSession(smartCardID); if (smartCardSession instanceof MyCryptoSmartCardSession) { final MyCryptoSmartCardSession mySmartCardSession = (MyCryptoSmartCardSession) smartCardSession; // We must provide the user authentication since we returned // true from providesUserAuthentication() // Also, the smart card PIN is required for private key // access. mySmartCardSession.loginPrompt(accessReason, operation); mySmartCardSession.signDecrypt(cryptoSystem, (MyCryptoTokenData) privateKeyData, input, inputOffset, output, outputOffset); return; } } throw new RuntimeException(); } catch (final SmartCardSessionClosedException e) { throw new CryptoTokenCancelException(e.toString()); } catch (final SmartCardCancelException e) { throw new CryptoTokenCancelException(e.toString()); } catch (final SmartCardRemovedException e) { throw new CryptoTokenCancelException(e.toString()); } catch (final SmartCardException e) { throw new CryptoTokenException(e.toString()); } finally { if (smartCardSession != null) { smartCardSession.close(); } } } }