/** * Copyright (C) 2011 Adriano Monteiro Marques * * Author: Zubair Nabi <zn.zubairnabi@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ package org.umit.icm.mobile.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import org.apache.commons.codec.binary.Base64; import org.umit.icm.mobile.process.Constants; import org.umit.icm.mobile.proto.MessageProtos.RSAKey; import android.os.Environment; /** * Provides methods for RSA asymmetric cryptography */ public class RSACrypto { /** * Returns an encrypted {@link String} of the Plain text passed. * Calls {@link RSACrypto#encryptPublic(PublicKey, byte[])}. Uses * {@link PublicKey} and {@link CryptoHelper#toHex(byte[])}. * @param plainText An object of the type {@link String} * @param publicKey An object of the type {@link PublicKey} * @return {@link String} * @see CryptoHelper */ public static String encryptPublic(PublicKey publicKey, String plainText) throws Exception { byte[] cipherText = encryptPublic(publicKey, plainText.getBytes()); return CryptoHelper.toHex(cipherText); } /** * Returns an decrypted {@link String} of the cipher text passed. * Calls {@link RSACrypto#decryptPrivate(PrivateKey, byte[])}. Uses * {@link PrivateKey} and {@link CryptoHelper#toByte(String)}. * @param cipherText An object of the type {@link String} * @param privateKey An object of the type {@link PrivateKey} * @return {@link String} * @see CryptoHelper */ public static String decryptPrivate(PrivateKey privateKey, String cipherText) throws Exception { byte[] cipherTextBytes = CryptoHelper.toByte(cipherText); return new String(decryptPrivate(privateKey, cipherTextBytes)); } /** * Returns an encrypted {@link String} of the Plain text passed. * Calls {@link RSACrypto#encryptPrivate(PrivateKey, byte[])}. Uses * {@link PrivateKey} and {@link CryptoHelper#toHex(byte[])}. * @param plainText An object of the type {@link String} * @param privateKey An object of the type {@link PrivateKey} * @return {@link String} * @see CryptoHelper */ public static String encryptPrivate(PrivateKey privateKey, String plainText) throws Exception { byte[] cipherText = encryptPrivate(privateKey, plainText.getBytes()); return CryptoHelper.toHex(cipherText); } /** * Returns a decrypted {@link String} of the cipher text passed. * Calls {@link RSACrypto#decryptPublic(PublicKey, byte[])}. Uses * {@link PublicKey} and {@link CryptoHelper#toByte(String)}. * @param cipherText An object of the type {@link String} * @param publicKey An object of the type {@link PublicKey} * @return {@link String} * @see CryptoHelper */ public static String decryptPublic(PublicKey publicKey, String cipherText) throws Exception { byte[] cipherTextBytes = CryptoHelper.toByte(cipherText); return new String(decryptPublic(publicKey, cipherTextBytes)); } /** * Returns an RSA KeyPair generated using * {@link KeyPairGenerator#generateKeyPair()}. * * @return {@link KeyPair} * @see KeyPairGenerator */ public static byte[] Sign(PrivateKey privateKey, byte[] data) throws Exception{ if(Constants.DEBUG_MODE) System.out.println("Signing the key inside RSACrypto#Sign"); Signature dsa = Signature.getInstance("SHA1withRSA"); dsa.initSign(privateKey); dsa.update(data); return dsa.sign(); } public static KeyPair generateKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(Constants.RSA_KEY_SIZE); return keyPairGen.generateKeyPair(); } /** * Returns an encrypted byte[] of the Plain text passed. * Uses{@link PublicKey} and {@link Cipher}. * * @param plainBytes An object of the type byte[] * @param publicKey An object of the type {@link PublicKey} * @return byte[] * @see Cipher */ public static byte[] encryptPublic(PublicKey publicKey, byte[] plainBytes) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(plainBytes); } /** * Returns an decrypted byte[] of the cipher bytes passed. * Uses {@link PrivateKey} and {@link Cipher}. * @param cipherBytes An object of the type byte[] * @param privateKey An object of the type {@link PrivateKey} * @return byte[] * @see Cipher */ public static byte[] decryptPrivate(PrivateKey privateKey, byte[] cipherBytes) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(cipherBytes); } /** * Returns an encrypted byte[] of the Plain text passed. * Uses{@link PrivateKey} and {@link Cipher}. * * @param plainBytes An object of the type byte[] * @param privateKey An object of the type {@link PrivateKey} * @return byte[] * @see Cipher */ public static byte[] encryptPrivate(PrivateKey privateKey, byte[] plainBytes) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(plainBytes); } /** * Returns an decrypted byte[] of the cipher bytes passed. * Uses {@link PublicKey} and {@link Cipher}. * @param cipherBytes An object of the type byte[] * @param publicKey An object of the type {@link PublicKey} * @return byte[] * @see Cipher */ public static byte[] decryptPublic(PublicKey publicKey, byte[] cipherBytes) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(cipherBytes); } /** * Writes an RSA key to disk. * @param fileName An object of the type {@link String} * @param modulus An object of the type {@link BigInteger} * @param exponential An object of the type {@link BigInteger} * @see SDCardReadWrite */ public static void saveKey(String fileName, BigInteger modulus, BigInteger exponential) throws IOException{ ObjectOutputStream objOutStream = null; File sdCard = Environment.getExternalStorageDirectory(); File keyDir = new File (sdCard.getAbsolutePath() + Constants.KEYS_DIR); keyDir.mkdirs(); File file = new File(keyDir, fileName); try { objOutStream = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream(file))); objOutStream.writeObject(modulus); objOutStream.writeObject(exponential); } finally { objOutStream.close(); } } /** * Reads an RSA {@link PublicKey} from disk. * @param fileName An object of the type {@link String} * @return {@link PublicKey} * @see SDCardReadWrite */ public static PublicKey readPublicKey(String fileName) throws IOException{ File sdCard = Environment.getExternalStorageDirectory(); File keyDir = new File (sdCard.getAbsolutePath() + Constants.KEYS_DIR); File file = new File(keyDir, fileName); InputStream inputStream = new FileInputStream(file.toString()); ObjectInputStream objInputStream = new ObjectInputStream(new BufferedInputStream(inputStream)); try { BigInteger modulus = (BigInteger) objInputStream.readObject(); BigInteger exponential = (BigInteger) objInputStream.readObject(); RSAPublicKeySpec rsaKeySpec = new RSAPublicKeySpec(modulus, exponential); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(rsaKeySpec); } catch (Exception e) { throw new RuntimeException("readPublicKey exception", e); } finally { objInputStream.close(); } } /** * Reads an RSA {@link PrivateKey} from disk. * * @param fileName An object of the type {@link String} * @return {@link PrivateKey} * @see SDCardReadWrite */ public static PrivateKey readPrivateKey(String fileName) throws IOException{ File sdCard = Environment.getExternalStorageDirectory(); File keyDir = new File (sdCard.getAbsolutePath() + Constants.KEYS_DIR); File file = new File(keyDir, fileName); InputStream inputStream = new FileInputStream(file.toString()); ObjectInputStream objInputStream = new ObjectInputStream(new BufferedInputStream(inputStream)); try { BigInteger modulus = (BigInteger) objInputStream.readObject(); BigInteger exponential = (BigInteger) objInputStream.readObject(); RSAPrivateKeySpec rsaKeySpec = new RSAPrivateKeySpec(modulus, exponential); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(rsaKeySpec); } catch (Exception e) { throw new RuntimeException("readPrivateKey exception", e); } finally { objInputStream.close(); } } /** * Converts a {@link String} to {@link PublicKey}. * @param publicKeyString An object of the type {@link String} * @return {@link PublicKey} * @see X509EncodedKeySpec * @see KeyFactory */ public static PublicKey stringToPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException { X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); } /** * Converts a {@link String} to {@link PrivateKey}. * @param privateKeyString An object of the type {@link String} * @return {@link PrivateKey} * @see PKCS8EncodedKeySpec * @see KeyFactory */ public static PrivateKey stringToPrivateKey(String privateKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException { PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(spec); } public static PrivateKey generatePrivateKey(BigInteger modulus, BigInteger exponential) throws NoSuchAlgorithmException, InvalidKeySpecException { RSAPrivateKeySpec rsaKeySpec = new RSAPrivateKeySpec(modulus, exponential); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(rsaKeySpec); } public static PublicKey generatePublicKey(BigInteger modulus, BigInteger exponential) throws NoSuchAlgorithmException, InvalidKeySpecException { RSAPublicKeySpec rsaKeySpec = new RSAPublicKeySpec(modulus, exponential); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(rsaKeySpec); } public static RSAKey getPublicKeyIntegers(PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class); RSAKey rsaKey = RSAKey.newBuilder() .setExp(publicKeySpec.getPublicExponent().toString()) .setMod(publicKeySpec.getModulus().toString()) .build(); return rsaKey; } }