Enterprise J2ME Developing Mobile Java Applications [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Enterprise J2ME Developing Mobile Java Applications [Electronic resources] - نسخه متنی

Michael Juntao Yuan

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



20.4 Public Key Encryption


Public key algorithms eliminate the key exchange problems associated with symmetric algorithms. Basically, each user has a pair of keys. The public key is available to anyone, and the private key is known only to the user. Public key algorithms are designed to be one-way trapdoor systems where a message encrypted by the public key can only be decrypted by the corresponding private key (see Figure 20.3). So, if Alice wants to send Bob a secret message, she uses Bob's public key to encrypt the data and sends it over any general insecure network. Only Bob himself has the correct private key to decrypt the message. The beauty of this scheme is that no secret key ever needs to be exchanged.


Figure 20.3. Public key algorithms are one-way trapdoors.


However, without built-in hardware acceleration or native long-integer support, public key encryption can be very slow on mobile devices. In reality, we often use public key algorithms to exchange symmetric keys and use symmetric key channels for further communication. Our demo MIDlet for RSA public key encryption and decryption is shown in Figure 20.4.


Figure 20.4. RSA public key encryption using the Phaos MF library.



20.4.1 Bouncy Castle


To generate an RSA key pair, BC requires the developer to pick a mathematical parameter called public exponent. A common pick is 65537 (Hex 10001) for strong security and fast performance. Listing 20.18 demonstrates how to generate an RSA key pair in the CryptoEngine class.

Listing 20.18. BC RSA key pair generation



public void generateRSAKeyPair () throws Exception {
SecureRandom sr = new SecureRandom();
BigInteger pubExp = new BigInteger("10001", 16);
RSAKeyGenerationParameters RSAKeyGenPara =
new RSAKeyGenerationParameters(pubExp, sr, 1024, 80);
RSAKeyPairGenerator RSAKeyPairGen = new RSAKeyPairGenerator();
RSAKeyPairGen.init(RSAKeyGenPara);
AsymmetricCipherKeyPair keyPair = RSAKeyPairGen.generateKeyPair();
RSAprivKey = (RSAPrivateCrtKeyParameters) keyPair.getPrivate();
RSApubKey = (RSAKeyParameters) keyPair.getPublic();
}

Listings 20.19 and 20.20 demonstrate how to serialize the key pair offline to disk files and then read them back. Please note that since BC does not support a good key serialization mechanism in its lightweight API package, we have to manually serialize all key parameters, which is tedious.

Listing 20.19. Serialize the key pair to disk files



BigInteger mod = RSAprivKey.getModulus();
out = new FileOutputStream(outdir + "RSAmod.dat");
out.write(mod.toByteArray());
out.flush(); out.close();
BigInteger privExp = RSAprivKey.getExponent();
out = new FileOutputStream(outdir + "RSAprivExp.dat");
out.write(privExp.toByteArray());
out.flush(); out.close();
pubExp = RSAprivKey.getPublicExponent();
if ( !pubExp.equals(new BigInteger("10001", 16)) )
throw new Exception("wrong public exponent");
out = new FileOutputStream(outdir + "RSApubExp.dat");
out.write(pubExp.toByteArray());
out.flush(); out.close();
BigInteger dp = RSAprivKey.getDP();
out = new FileOutputStream(outdir + "RSAdp.dat");
out.write(dp.toByteArray());
out.flush(); out.close();
BigInteger dq = RSAprivKey.getDQ();
out = new FileOutputStream(outdir + "RSAdq.dat");
out.write(dq.toByteArray());
out.flush(); out.close();
BigInteger p = RSAprivKey.getP();
out = new FileOutputStream(outdir + "RSAp.dat");
out.write(p.toByteArray());
out.flush(); out.close();
BigInteger q = RSAprivKey.getQ();
out = new FileOutputStream(outdir + "RSAq.dat");
out.write(q.toByteArray());
out.flush(); out.close();
BigInteger qInv = RSAprivKey.getQInv();
out = new FileOutputStream(outdir + "RSAqInv.dat");
out.write(qInv.toByteArray());
out.flush(); out.close();

Listing 20.20. Deserialize the key pair in CryptoEngine constructor


Class c = this.getClass();
InputStream is;
is = c.getResourceAsStream("/keys/RSAmod.dat");
BigInteger RSAmod = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAprivExp.dat");
BigInteger RSAprivExp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSApubExp.dat");
BigInteger RSApubExp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAdp.dat");
BigInteger RSAdp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAdq.dat");
BigInteger RSAdq = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAp.dat");
BigInteger RSAp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAq.dat");
BigInteger RSAq = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAqInv.dat");
BigInteger RSAqInv = new BigInteger(readFromStream(is));
is.close();
RSAprivKey = new RSAPrivateCrtKeyParameters(
RSAmod, RSApubExp, RSAprivExp, RSAp,
RSAq, RSAdp, RSAdq, RSAqInv);
RSApubKey = new RSAKeyParameters(false, RSAmod, RSApubExp);

Now, we can encrypt and decrypt messages using the public key RSApubKey and private key RSAprivKey respectively (Listing 20.21).

Listing 20.21. RSA encryption and decryption with BC



// Public key encrypt using RSA
public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {
if (RSApubKey == null)
throw new Exception("Generate RSA keys first!");
AsymmetricBlockCipher eng = new RSAEngine();
eng = new PKCS1Encoding(eng);
eng.init(true, RSApubKey);
return eng.processBlock(toEncrypt, 0, toEncrypt.length);
}
// private key decrypt
public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {
if (RSAprivKey == null)
throw new Exception("Generate RSA keys first!");
AsymmetricBlockCipher eng = new RSAEngine();
eng = new PKCS1Encoding(eng);
eng.init(false, RSAprivKey);
return eng.processBlock(toDecrypt, 0, toDecrypt.length);
}


20.4.2 IAIK JCE-ME


Listing 20.22 illustrates how to generate an RSA key pair using the IAIK library.

Listing 20.22. Generate RSA keys using the IAIK library



public void generateRSAKeyPair () throws Exception {
RSAKeyPairGenerator rsaKeyPairGenerator = new RSAKeyPairGenerator();
rsaKeyPairGenerator.initialize(1024, null, null);
CryptoBag cryptoBag = rsaKeyPairGenerator.generateKeyPair();
RSApubKey =
(PublicKey) cryptoBag.getCryptoBag(cryptoBag.V_KEY_PUBLIC);
RSAprivKey =
(PrivateKey) cryptoBag.getCryptoBag(cryptoBag.V_KEY_PRIVATE);
return;
}

Listings 20.23 and 20.24 demonstrate the serialization and deserialization processes in the GenerateAllKeys class and the CryptoEngine constructor.

Listing 20.23. Serialize the keys to a disk file



out = new FileOutputStream(outdir + "RSApub.dat");
out.write(RSApubKey.getEncoded());
out.flush(); out.close();
out = new FileOutputStream(outdir + "RSApriv.dat");
out.write(RSAprivKey.getEncoded());
out.flush(); out.close();

Listing 20.24. Deserialize the RSA key pair


Class c = this.getClass();
InputStream is;
byte [] asnArray;
is = c.getResourceAsStream("/keys/RSApub.dat");
asnArray = readFromStream(is);
is.close();
RSApubKey = new PublicKey(new ASN1(asnArray));
is = c.getResourceAsStream("/keys/RSApriv.dat");
asnArray = readFromStream(is);
is.close();
RSAprivKey = new PrivateKey(new ASN1(asnArray));

With the key pair, we can now encrypt and decrypt messages (Listing 20.25). Notice how simple the API is!

Listing 20.25. RSA encryption and decryption



// Public key encrypt using RSA
public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {
if (RSApubKey == null)
throw new Exception("Generate RSA keys first!");
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsa.init(Cipher.ENCRYPT_MODE, RSApubKey, null, null);
return rsa.doFinal(toEncrypt);
}
// private key decrypt
public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {
if (RSAprivKey == null)
throw new Exception("Generate RSA keys first!");
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsa.init(Cipher.DECRYPT_MODE, RSAprivKey, null, null);
return rsa.doFinal(toDecrypt);
}


20.4.3 Phaos Micro Foundation


Using the Phaos MF library, generating an RSA key pair is easy (Listing 20.26).

Listing 20.26. Generate a RSA key pair using Phaos MF



public void generateRSAKeyPair () throws Exception {
RSAKeyGenParams params = new RSAKeyGenParams(1024);
KeyPairGenerator kpg =
KeyPairGenerator.getInstance(
OIDList.RSA_ENCRYPTION, params);
KeyPair kp = kpg.generateKeyPair();
RSApubKey = (RSAPublicKey)kp.publicKey;
RSAprivKey = (RSAPrivateKey)kp.privateKey;
return;
}

Serializing and deserializing keys in the Phaos library are demonstrated in Listings 20.27 and 20.28.

Listing 20.27. Serialize RSA keys to files



RSApubKey.output(new FileOutputStream(outdir + "RSApubKey.der"));
RSAprivKey.output(new FileOutputStream(outdir + "RSAprivKey.der"));

Listing 20.28. Deserialize keys from the files


Class c = this.getClass();
InputStream is;
is = c.getResourceAsStream("/keys/RSApubKey.der");
RSApubKey = new RSAPublicKey(is);
is.close();
is = c.getResourceAsStream("/keys/RSAprivKey.der");
RSAprivKey = new RSAPrivateKey(is);
is.close();

Encryption and decryption are demonstrated in Listing 20.29.

Listing 20.29. RSA encryption and decryption



public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {
if (RSApubKey == null)
throw new Exception("Generate RSA keys first!");
byte [] encrypted;
PooledArray plaintext = ByteArrayPool.getArray(toEncrypt);
AlgorithmIdentifier algID = new OAEPAlgID();
Cipher cipher = Cipher.getInstance(algID, RSApubKey);
PooledArray ciphertext =
cipher.encrypt(plaintext.buffer, 0, plaintext.length);
ByteArrayOutputStream out = new ByteArrayOutputStream ();
algID.output(out);
out.write(ciphertext.buffer, 0, ciphertext.length);
encrypted = out.toByteArray();
out.close();
plaintext.release();
ciphertext.release();
return encrypted;
}
public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {
if (RSAprivKey == null)
throw new Exception("Generate RSA keys first!");
byte [] decrypted;
ByteArrayInputStream in = new ByteArrayInputStream(toDecrypt);
AlgorithmIdentifier algID = new AlgorithmIdentifier(in);
PooledArray ciphertext = ByteArrayPool.getArray(in.available());
in.read(ciphertext.buffer, 0, ciphertext.length);
in.close();
Cipher cipher = Cipher.getInstance(algID, RSAprivKey);
PooledArray plaintext =
cipher.decrypt(ciphertext.buffer, 0, ciphertext.length);
decrypted = plaintext.toByteArray();
plaintext.release();
ciphertext.release();
return decrypted;
}


20.4.4 NTRU jNeo


Generating an NTRU key pair for the NTRUEncrypt algorithm takes only one line of code. The following code snippet generates an NTRU 251-bit encryption key, which has cryptographic strength equivalent to a 1,024-bit RSA key (Listing 20.30).

Listing 20.30. Generate the NTRU encryption key pair



private RandomNumber rn =
new RandomNumber(NTRUConst.NTRU_SHA1_HASH);
private Context ctx = new Context(rn);
// ... ...
public void generateNTRUencKeys () throws Exception {
NTRUencKeys = new EncKeys(ctx,
NTRUConst.NTRU_KEYSTRENGTH_251,
NTRUConst.NTRU_SHA1_HASH);
return;
}

NTRU jNeo provides its own key serialization methods. Let's see how it is done in the GenerateAllKeys class (Listing 20.31) and the CryptoEngine (Listing 20.32) constructor.

Listing 20.31. Serialize the key pair to disk files



byte [] pubKey = NTRUencKeys.exportPubKey(null, 0, true);
out = new FileOutputStream(outdir + "EncPubKey.dat");
out.write(pubKey); out.flush(); out.close();
byte [] privKey = NTRUencKeys.exportPrivKey(null, 0, true);
out = new FileOutputStream(outdir + "EncPrivKey.dat");
out.write(privKey); out.flush(); out.close();

Listing 20.32. Deserialize the key pair from disk files


Class c = this.getClass();
InputStream is;
is = c.getResourceAsStream("/keys/EncPubKey.dat");
byte [] encPubKeyData = readFromStream(is);
is.close();
is = c.getResourceAsStream("/keys/EncPrivKey.dat");
byte [] encPrivKeyData = readFromStream(is);
is.close();
NTRUencKeys = new EncKeys (encPubKeyData, o,
encPubKeyData.length,
encPrivKeyData, 0,
encPrivKeyData.length,
true);

The encryption and decryption methods are demonstrated in Listing 20.33. Notice that we take care of the proper block handling in our methods.

Listing 20.33. Encryption and decryption using the NTRU algorithm



public byte [] NTRUEncrypt (byte [] toEncrypt) throws Exception {
if ( NTRUencKeys == null )
throw new Exception("Generate keys first!");
int cipherBlockSize =
NTRUencKeys.ciphertextSize(NTRUConst.NTRU_KEYSTRENGTH_251);
int plainBlockSize =
NTRUencKeys.blockSize(NTRUConst.NTRU_KEYSTRENGTH_251);
byte [] cipherBlock = new byte [cipherBlockSize];
byte [] plainBlock = new byte [plainBlockSize];
int psize;
ByteArrayInputStream bais = new ByteArrayInputStream(toEncrypt);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ( (psize = bais.read(plainBlock)) != -1 ) {
// resize the last plain text block
if ( psize < plainBlockSize ) {
byte tmp [] = new byte [psize];
for (int i = 0; i < psize; i++) {
tmp[i] = plainBlock[i];
}
plainBlock = tmp;
}
NTRUencKeys.blockEncrypt(ctx, plainBlock, 0,
plainBlock.length, cipherBlock, 0);
baos.write(cipherBlock);
}
baos.flush();
byte [] result = baos.toByteArray();
baos.close(); bais.close();
return result;
}
public byte [] NTRUDecrypt (byte [] toDecrypt) throws Exception {
if ( NTRUencKeys == null )
throw new Exception("Generate keys first!");
int cipherBlockSize =
NTRUencKeys.ciphertextSize(NTRUConst.NTRU_KEYSTRENGTH_251);
int plainBlockSize =
NTRUencKeys.blockSize(NTRUConst.NTRU_KEYSTRENGTH_251);
byte [] cipherBlock = new byte [cipherBlockSize];
byte [] plainBlock = new byte [plainBlockSize];
int psize;
ByteArrayInputStream bais = new ByteArrayInputStream(toDecrypt);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ( bais.read(cipherBlock) != -1 ) {
psize = NTRUencKeys.blockDecrypt(ctx,
cipherBlock, 0,
cipherBlock.length,
plainBlock, 0);
// resize the last plain text block
if ( psize < plainBlockSize ) {
byte tmp [] = new byte [psize];
for (int i = 0; i < psize; i++) {
tmp[i] = plainBlock[i];
}
plainBlock = tmp;
}
baos.write(plainBlock);
}
baos.flush();
byte [] result = baos.toByteArray();
baos.close(); bais.close();
return result;
}


/ 204