BouncyCastle J2ME RSA using custom keys

2019-05-26 19:33发布

问题:

I would like to use BouncyCastle J2ME/RIM Crypto in my Blackberry Application.

The issue i'm having is that I would like to generate the public key for encryption from a C#.NET program that sends the key to the BlackBerry.

Is it possible to encrypt a message using a raw string? Also, do I need to know other common variable such as modulo etc? Apologies but i'm completely new to cryptography algorithms.

Do I need BouncyCastle for this or can the above be done with RIM Crypto?

Thanks, Conor

回答1:

I did it using bouncycastle, but with RIM Crypto is similar. Follow the example. As you can see the keys are strings ... :

    public CypherDecypherExample()
   {        
    String plain    ="a plain string";
    String cipher   = null;
    String decipher = null;
    byte [] byte_cipher = null;
    byte [] byte_plain  = null;

    // key           |-- 128 bit -->|-- 256 bit --->|
    String key    = "aaaaaaaaaaaaaaaacccccccccccccccc";
    String iv     = "bbbbbbbbbbbbbbbb";

System.out.println("bouncycastle.plain: " + plain);

    try {
        byte_cipher = encrypt(plain.getBytes(), key.getBytes(), iv.getBytes());
        cipher = new String(byte_cipher);
        System.out.println("bouncycastle.cipher: " + cipher);

    } catch (Exception e) {
        e.printStackTrace();
    }


    try {
        byte_plain = decrypt(byte_cipher, key.getBytes(), iv.getBytes());
        decipher = new String(byte_plain);
        System.out.println("bouncycastle.decipher: " + decipher);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

} 

private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
    String plain = new String(data);
    System.out.println("bouncycastle.cipherData: " + plain);

    int minSize = cipher.getOutputSize(data.length);
    byte[] outBuf = new byte[minSize];
    int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
    int length2 = cipher.doFinal(outBuf, length1);
    int actualLength = length1 + length2;
    byte[] result = new byte[actualLength];
    System.arraycopy(outBuf, 0, result, 0, result.length);

System.out.println("bouncycastle.cipherData returning");

    return result;
}

private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
    PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher((BlockCipher) new CBCBlockCipher(
            new AESEngine()));
    CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
    aes.init(false, ivAndKey);
    return cipherData(aes, cipher);
}

private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{       
    PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
            new AESEngine()));

    CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);

    aes.init(true, ivAndKey);

    return cipherData(aes, plain);
}


回答2:

An RSA public key consists of two components, not just one like I thought.

There is the Exponent and Modulus. These are both number but I pass them to the Blackberry from .NET client as Base64 strings and decode them into byte arrays when be used by the RIM Crypto function as they take Byte arrays as parameters.

byte[] exponent = Base64InputStream.decode("exponent base64 string");
byte[] modulus = Base64InputStream.decode("modulus base64 string");

NoCopyByteArrayOutputStream cipherUserData = new NoCopyByteArrayOutputStream();             
RSACryptoSystem cryptoSystem = new RSACryptoSystem(1024);

// Create Public key using your variables from before
RSAPublicKey publicKey = new RSAPublicKey( cryptoSystem, exponent, modulus);

// Encryption engine objects
RSAEncryptorEngine eEngine = new RSAEncryptorEngine(publicKey);
PKCS1FormatterEngine fEngine = new PKCS1FormatterEngine(eEngine);
BlockEncryptor cryptoStream = new BlockEncryptor(fEngine, cipherUserData);  


// Read the user data and encrypt while doing so. Remember, cryptoStream writes its data to
// cipherUserData so this is where the encrypted version of userData will end up.
cryptoStream.write( userData, 0, userData.length );

cryptoStream.close();
cipherUserData.close();

String encryptedUserData = new String(cipherUserData.toByteArray());

That's pretty much all there is too it folks, it's straightforward but it took me a long time to get this from the API docs :)

Important note RSA is limited for encryption purposes in that you can only encrypt a message that <= key size. That is 117 bytes for 1024 Bit RSA and 245 bytes for 2048 RSA. To encrypt larger messages the accepted way is to encrypt the message using AES or similar then encrypt the AES key with the RSA public key. You will the send the AES ciphertext and also the RSA ciphertext containing the key to decrypt the AES ciphertext.

What I have written above took days of tinkering and reading. I hope it helps somebody achieve their goal faster than that. :)