/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* src/com/vodafone360/people/VODAFONE.LICENSE.txt or
* http://github.com/360/360-Engine-for-Android
* See the License for the specific language governing permissions and
* limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved.
* Use is subject to license terms.
*/
package com.vodafone360.people.engine.login;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import com.vodafone360.people.datatypes.PublicKeyDetails;
/**
* Utility class for encrypting/decrypting login and registration user
* credentials.
*/
public class RSAEncryptionUtils {
/**
* Default public key which can be used to encrypt data. The server has the
* suitable private key for decrypting the data. This is the modulo part.
*/
private static final byte[] DEFAULT_PUBKEY_MODULO = new byte[] {
0, -93, -23, 8, -55, 66, -20, 39, 17, 40, -86, 22, -120, -78, -13, 24, -35, 79, -49,
-64, -104, -31, -125, -3, 39, 2, 88, 41, -70, 47, -41, -99, -15, 40, -105, -27, -95,
-99, 99, -32, 44, 4, -18, -40, -5, -87, 82, -33, 40, -4, 122, 15, 7, 3, -73, -84, 13,
32, -115, 41, 51, 27, 101, 95, -120, 65, 16, 16, 40, 57, -64, -12, 8, 17, 80, -80, 21,
98, 97, 40, -100, 114, 90, 121, 24, -11, -47, -33, 85, -16, 67, -2, -87, -18, -59, 24,
-83, 127, -123, -99, -39, -35, 111, -90, 27, -9, -64, -111, -116, -71, -82, 5, -116,
73, -38, 117, -39, -113, -16, -115, 37, 5, -128, 68, 108, -106, 82, -44, 9
};
/**
* Exponential part of the public key.
*/
private static final byte[] DEFAULT_PUBKEY_EXPONENTIAL = new byte[] {
1, 0, 1
};
/**
* Fetches the default public key.
*
* @return Public key
*/
protected static RSAKeyParameters getDefaultPublicKey() {
return new RSAKeyParameters(false, new BigInteger(DEFAULT_PUBKEY_MODULO), new BigInteger(
DEFAULT_PUBKEY_EXPONENTIAL));
}
/**
* Copies the default key into the given parameter.
*
* @param key Where the key should be copied.
*/
public static void copyDefaultPublicKey(PublicKeyDetails key) {
key.mExponential = DEFAULT_PUBKEY_EXPONENTIAL;
key.mModulus = DEFAULT_PUBKEY_MODULO;
}
/**
* Composes a RSA Public Key from its components.
*
* @param mod the RSA modulo.
* @param exp the RSA exponent.
* @return the RSA public key.
*/
protected static RSAKeyParameters getRSAPubKey(final byte[] mod, final byte[] exp) {
return new RSAKeyParameters(false, new BigInteger(mod), new BigInteger(exp));
}
/**
* Encrypts bytes with the given RSA Public Key.
*
* @param pubKey the RSA Public Key.
* @param data the data to encrypt.
* @return the encrypted data.
* @throws InvalidParameterException
* @throws InvalidCipherTextException
*/
protected static byte[] encryptRSA(final RSAKeyParameters pubKey, final String data)
throws InvalidCipherTextException {
if (data == null) {
throw new InvalidParameterException("RSAEncryptionUtils.encryptRSA() "
+ "data cannot be NULL");
}
try {
return rsa(true, pubKey, data.getBytes("utf-8"));
} catch (final UnsupportedEncodingException e) {
return rsa(true, pubKey,data.getBytes());
}
}
/**
* Ensures data length is always a multiple of 16 (by rounding up if
* necessary).
*/
private static final int ROUND_UP_VALUE = 16;
/**
* Encrypts or Decrypts bytes with the given RSA Public or Private Key.
*
* @param encrypt true for encrypt, false for decrypt.
* @param key the RSA Public or Private Key.
* @param data the data to encrypt or decrypt.
* @return the encrypted or decrypted data.
*/
private static byte[] rsa(final boolean encrypt, final RSAKeyParameters key, final byte[] data)
throws InvalidCipherTextException {
final byte[] dataAligned = new byte[roundUp(data.length, ROUND_UP_VALUE)];
System.arraycopy(data, 0, dataAligned, 0, data.length);
final RSAEngine rsa = new RSAEngine();
final AsymmetricBlockCipher pkcs1 = new PKCS1Encoding(rsa);
pkcs1.init(encrypt, key);
if (encrypt)
return pkcs1.processBlock(dataAligned, 0, dataAligned.length);
return trimZeros(pkcs1.processBlock(dataAligned, 0, dataAligned.length));
}
/**
* Return the value of v rounded up to a multiple of t
*
* @param v The value to round up
* @param t The multiple to use
* @return The rounded result
*/
private static int roundUp(final int v, final int t) {
if (v % t == 0)
return v;
return (v / ROUND_UP_VALUE + 1) * ROUND_UP_VALUE;
}
/**
* Removes all the zeros from the end of the given array.
*
* @param data Initial array to trim
* @return New array which n less elements (where n = number of trailing
* zeros). If the given array is all zeros then the function will
* fail and the result will be the initial array (NULL if initial
* array is NULL)
*/
private static byte[] trimZeros(final byte[] data) {
if (data == null)
return null;
if (data.length > 0 && data[data.length - 1] == 0) {
byte[] result;
for (int i = data.length - 1; i >= 0; i--)
if (data[i] != 0) {
result = new byte[i + 1];
System.arraycopy(data, 0, result, 0, result.length);
return result;
}
}
return data;
}
}