/* * Copyright 2011 Licel LLC. * * 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. */ package com.licel.jcardsim.crypto; import java.security.SecureRandom; import javacard.security.CryptoException; import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.PrivateKey; import javacard.security.PublicKey; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.generators.DSAKeyPairGenerator; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; /** * Implementation * <code>KeyPair</code> based on BouncyCastle CryptoAPI. * * @see KeyPair * @see RSAKeyPairGenerator * @see DSAKeyPairGenerator * @see ECKeyPairGenerator */ public final class KeyPairImpl { byte algorithm; short keyLength; AsymmetricCipherKeyPairGenerator engine; PrivateKey privateKey; PublicKey publicKey; SecureRandom rnd = new SecureRandom(); KeyGenerationParameters keyGenerationParameters; /** * (Re)Initializes the key objects encapsulated in this * <code>KeyPair</code> instance with new key values. The initialized public * and private key objects encapsulated in this instance will then be * suitable for use with the * <code>Signature</code>, * <code>Cipher</code> and * <code>KeyAgreement</code> objects. An internal secure random number * generator is used during new key pair generation. <p>Notes:<ul> * <li><em>For the RSA algorithm, if the exponent value in the public key * object is pre-initialized, it will be retained. Otherwise, a default * value of 65537 will be used.</em> <li><em>For the DSA algorithm, if the * p, q and g parameters of the public key object are pre-initialized, they * will be retained. Otherwise, default precomputed parameter sets will be * used. The required default precomputed values are listed in </em>Appendix * B<em> of </em>Java Cryptography Architecture API Specification & * Reference<em> document.</em> <li><em>For the EC case, if the Field, A, B, * G and R parameters of the key pair are pre-initialized, then they will be * retained. Otherwise default pre-specified values MAY be used (e.g. WAP * predefined curves), since computation of random generic EC keys is * infeasible on the smart card platform.</em> <li><em>If the time taken to * generate the key values is excessive, the implementation may * automatically request additional APDU processing time from the CAD.</em> * </ul> * * @throws CryptoException with the following reason codes:<ul> * <li><code>CryptoException.ILLEGAL_VALUE</code> if the exponent value * parameter in RSA or the p, q, g parameter set in DSA or the Field, A, B, * G and R parameter set in EC is invalid. </ul> * @see javacard.framework.APDU * @see javacard.security.Signature * @see javacardx.crypto.Cipher * @see javacard.security.RSAPublicKey * @see javacard.security.ECKey * @see javacard.security.DSAKey */ public final void genKeyPair() throws CryptoException { initEngine(); createKeys(); AsymmetricCipherKeyPair kp = engine.generateKeyPair(); ((KeyWithParameters)publicKey).setParameters(kp.getPublic()); ((KeyWithParameters)privateKey).setParameters(kp.getPrivate()); } /** * Constructs a * <code>KeyPair</code> instance for the specified algorithm and keylength; * the encapsulated keys are uninitialized. To initialize the * <code>KeyPair</code> instance use the * <code>genKeyPair()</code> method.<p> The encapsulated key objects are of * the specified * <code>keyLength</code> size and implement the appropriate * <code>Key</code> interface associated with the specified algorithm * (example - * <code>RSAPublicKey</code> interface for the public key and * <code>RSAPrivateKey</code> interface for the private key within an * <code>ALG_RSA</code> key pair). * <p>Notes:</p> <p><em>The key objects * encapsulated in the generated </em><code>KeyPair</code><em> object need * not support the </em><code>KeyEncryption</code><em> interface.</em></p> * * @param algorithm the type of algorithm whose key pair needs to be * generated. Valid codes listed in <code>ALG_..</code> constants above. * {@link KeyPair} * @param keyLength the key size in bits. The valid key bit lengths are key * type dependent. See the <code>KeyBuilder</code> class. * @see KeyBuilder * @throws CryptoException with the following reason codes:<ul> * <li><code>CryptoException.NO_SUCH_ALGORITHM</code> if the requested * algorithm associated with the specified type, size of key is not * supported.</ul> * @see KeyBuilder * @see javacard.security.Signature * @see javacardx.crypto.KeyEncryption * @see javacardx.crypto.Cipher */ public KeyPairImpl(byte algorithm, short keyLength) throws CryptoException { this.algorithm = algorithm; this.keyLength = keyLength; createKeys(); } /** * Constructs a new * <code>KeyPair</code> object containing the specified public key and * private key. <p>Note that this constructor only stores references to the * public and private key components in the generated * <code>KeyPair</code> object. It does not throw an exception if the key * parameter objects are uninitialized. * * @param publicKey the public key. * @param privateKey the private key. * @throws CryptoException with the following reason codes:<ul> * <li><code>CryptoException.ILLEGAL_VALUE</code> if the input parameter key * objects are inconsistent with each other - i.e mismatched algorithm, size * etc. <li><code>CryptoException.NO_SUCH_ALGORITHM</code> if the algorithm * associated with the specified type, size of key is not supported. </ul> */ public KeyPairImpl(PublicKey publicKey, PrivateKey privateKey) throws CryptoException { if (publicKey == null && privateKey == null) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } if ((publicKey != null) && !(publicKey instanceof KeyWithParameters)) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } this.publicKey = publicKey; if (this.publicKey != null) { selectAlgorithmByType(this.publicKey.getType()); } if ((privateKey != null) && !(privateKey instanceof KeyWithParameters)) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } this.privateKey = privateKey; if (this.privateKey != null) { selectAlgorithmByType(this.privateKey.getType()); } } /** * Returns a reference to the public key component of this * <code>KeyPair</code> object. * * @return a reference to the public key. */ public PublicKey getPublic() { return publicKey; } /** * Returns a reference to the private key component of this * <code>KeyPair</code> object. * * @return a reference to the private key. */ public PrivateKey getPrivate() { return privateKey; } private void selectAlgorithmByType(byte keyType) { switch (keyType) { case KeyBuilder.TYPE_RSA_PRIVATE: case KeyBuilder.TYPE_RSA_PUBLIC: algorithm = KeyPair.ALG_RSA; break; case KeyBuilder.TYPE_RSA_CRT_PRIVATE: algorithm = KeyPair.ALG_RSA_CRT; break; case KeyBuilder.TYPE_EC_F2M_PUBLIC: case KeyBuilder.TYPE_EC_F2M_PRIVATE: algorithm = KeyPair.ALG_EC_F2M; break; case KeyBuilder.TYPE_EC_FP_PUBLIC: case KeyBuilder.TYPE_EC_FP_PRIVATE: algorithm = KeyPair.ALG_EC_FP; break; case KeyBuilder.TYPE_DSA_PUBLIC: case KeyBuilder.TYPE_DSA_PRIVATE: algorithm = KeyPair.ALG_DSA; break; } } /** * Init key pair generation engine */ private void initEngine() { // only public key params, see specification if (publicKey != null) { keyGenerationParameters = ((KeyImpl) publicKey).getKeyGenerationParameters(rnd); } switch (algorithm) { case KeyPair.ALG_RSA: case KeyPair.ALG_RSA_CRT: if (keyGenerationParameters == null) { keyGenerationParameters = RSAKeyImpl.getDefaultKeyGenerationParameters(keyLength, rnd); } engine = new RSAKeyPairGenerator(); break; // case KeyPair.ALG_DSA: if (keyLength < 512 || keyLength > 1024 || keyLength % 64 != 0) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } if (keyGenerationParameters == null) { keyGenerationParameters = DSAKeyImpl.getDefaultKeyGenerationParameters(keyLength, rnd); } engine = new DSAKeyPairGenerator(); break; // ecc case KeyPair.ALG_EC_F2M: case KeyPair.ALG_EC_FP: if (keyGenerationParameters == null) { keyGenerationParameters = ECKeyImpl.getDefaultKeyGenerationParameters(algorithm, keyLength, rnd); } engine = new ECKeyPairGenerator(); break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } engine.init(keyGenerationParameters); } /* * Create uninitialized keys */ private void createKeys() { byte privateKeyType = 0; byte publicKeyType = 0; switch (algorithm) { case KeyPair.ALG_RSA: publicKeyType = KeyBuilder.TYPE_RSA_PUBLIC; privateKeyType = KeyBuilder.TYPE_RSA_PRIVATE; break; case KeyPair.ALG_RSA_CRT: publicKeyType = KeyBuilder.TYPE_RSA_PUBLIC; privateKeyType = KeyBuilder.TYPE_RSA_CRT_PRIVATE; break; case KeyPair.ALG_EC_FP: publicKeyType = KeyBuilder.TYPE_EC_FP_PUBLIC; privateKeyType = KeyBuilder.TYPE_EC_FP_PRIVATE; break; case KeyPair.ALG_EC_F2M: publicKeyType = KeyBuilder.TYPE_EC_F2M_PUBLIC; privateKeyType = KeyBuilder.TYPE_EC_F2M_PRIVATE; break; case KeyPair.ALG_DSA: publicKeyType = KeyBuilder.TYPE_DSA_PUBLIC; privateKeyType = KeyBuilder.TYPE_DSA_PRIVATE; break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } if(publicKey!=null && keyLength == 0){ keyLength = publicKey.getSize(); } if(publicKey == null){ publicKey = (PublicKey) KeyBuilder.buildKey(publicKeyType, keyLength, false); } if(privateKey == null){ privateKey = (PrivateKey) KeyBuilder.buildKey(privateKeyType, keyLength, false); } } }