8.4.3 实现

很遗憾,作为常用非对称加密算法的ElGamal算法并没有出现在Java 6的API中,但却包含在了Bouncy Castle的API中,弥补了Java语言缺少对于ElGamal算法支持的缺憾。

有关ElGamal算法的Bouncy Castle实现细节如表8-3所示。

figure_0292_0071

JCE框架为其他非对称加密算法实现提供了一个构建密钥对方式,均基于DH算法参数材料—DHParameterSpec类。代码清单8-10展示了如何构建ElGamal算法密钥对。

代码清单8-10 构建ElGamal算法密钥对


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//省略

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//实例化算法参数生成器

AlgorithmParameterGenerator apg=AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);

//初始化算法参数生成器

apg.init(KEY_SIZE);

//生成算法参数

AlgorithmParameters params=apg.generateParameters();

//构建参数材料

DHParameterSpec elParams=(DHParameterSpec)params.getParameterSpec(DHParameterSpec.class);

//实例化密钥对生成器

KeyPairGenerator kpg=KeyPairGenerator.getInstance(KEY_ALGORITHM);

//初始化密钥对生成器

kpg.initialize(elParams, new SecureRandom());

//生成密钥对

KeyPair keys=kpg.genKeyPair();


有了密钥对实例化对象keys自然就可以获得相应的密钥了。

需要注意的是,这里我们使用Bouncy Castle提供的ElGamal算法实现,在使用各种引擎类做实例化操作前,需导入Bouncy Castle提供者,如代码清单8-11所示。

代码清单8-11 导入Bouncy Castle提供者


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//省略

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());


抛开密钥对的生成实现,ElGamal算法实现与RSA算法实现几乎一致,如代码清单8-12所示。

代码清单8-12 ElGamal算法实现


import java.security.AlgorithmParameterGenerator;

import java.security.AlgorithmParameters;

import java.security.Key;

import java.security.KeyFactory;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.SecureRandom;

import java.security.Security;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

import java.util.HashMap;

import java.util.Map;

import javax.crypto.Cipher;

import javax.crypto.spec.DHParameterSpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**

*ElGamal安全编码组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class ElGamalCoder{

//非对称加密密钥算法

public static final String KEY_ALGORITHM="ElGamal";

/**

*密钥长度

*ElGamal算法

*默认密钥长度为1024

*密钥长度范围在160~16384位不等,

*且密钥长度必须是8的倍数。

*/

private static final int KEY_SIZE=256;

//公钥

private static final String PUBLIC_KEY="ElGamalPublicKey";

//私钥

private static final String PRIVATE_KEY="ElGamalPrivateKey";

/**

*用私钥解密

*@param data待解密数据

*@param key私钥

*@return byte[]解密数据

*@throws Exception

*/

public static byte[]decryptByPrivateKey(byte[]data, byte[]key)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//私钥材料转换

PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);

//实例化密钥工厂

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

//生成私钥

Key privateKey=keyFactory.generatePrivate(pkcs8KeySpec);

//对数据解密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE, privateKey);

return cipher.doFinal(data);

}

/**

*用公钥加密

*@param data待加密数据

*@param key公钥

*@return byte[]加密数据

*@throws Exception

*/

public static byte[]encryptByPublicKey(byte[]data, byte[]key)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//公钥材料转换

X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);

//实例化密钥工厂

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

//生成公钥

Key publicKey=keyFactory.generatePublic(x509KeySpec);

//对数据加密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.ENCRYPT_MODE, publicKey);

return cipher.doFinal(data);

}

/**

*生成密钥

*@return Map密钥Map

*@throws Exception

*/

public static Map<String, Object>initKey()throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//实例化算法参数生成器

AlgorithmParameterGenerator apg=AlgorithmParameterGenerator.getInstance

(KEY_ALGORITHM);

//初始化算法参数生成器

apg.init(KEY_SIZE);

//生成算法参数

AlgorithmParameters params=apg.generateParameters();

//构建参数材料

DHParameterSpec elParams=(DHParameterSpec)params.getParameterSpec

(DHParameterSpec.class);

//实例化密钥对生成器

KeyPairGenerator kpg=KeyPairGenerator.getInstance(KEY_ALGORITHM);

//初始化密钥对生成器

kpg.initialize(elParams, new SecureRandom());

//生成密钥对

KeyPair keys=kpg.genKeyPair();

//取得密钥

PublicKey publicKey=keys.getPublic();

PrivateKey privateKey=keys.getPrivate();

//封装密钥

Map<String, Object>map=new HashMap<String, Object>(2);

map.put(PUBLIC_KEY, publicKey);

map.put(PRIVATE_KEY, privateKey);

return map;

}

/**

*取得私钥

*@param keyMap密钥Map

*@return byte[]私钥

*@throws Exception

*/

public static byte[]getPrivateKey(Map<String, Object>keyMap)throws Exception{

Key key=(Key)keyMap.get(PRIVATE_KEY);

return key.getEncoded();

}

/**

*取得公钥

*@param keyMap

*@return

*@throws Exception

*/

public static byte[]getPublicKey(Map<String, Object>keyMap)throws Exception{

Key key=(Key)keyMap.get(PUBLIC_KEY);

return key.getEncoded();

}

}


ElGamal算法的测试用例与RSA算法测试用例较为相近。不同的是,ElGamal算法实现仅有“公钥加密,私钥解密”的部分。测试用例如代码清单8-13所示。

代码清单8-13 ElGamal算法实现测试用例


import static org.junit.Assert.*;

import java.util.Map;

import org.apache.commons.codec.binary.Base64;

import org.junit.Before;

import org.junit.Test;

/**

*ElGamal校验

*@author梁栋

*@version 1.0

*/

public class ElGamalCoderTest{

//公钥

private byte[]publicKey;

//私钥

private byte[]privateKey;

/**

*初始化密钥

*@throws Exception

*/

@Before

public void initKey()throws Exception{

Map<String, Object>keyMap=ElGamalCoder.initKey();

publicKey=ElGamalCoder.getPublicKey(keyMap);

privateKey=ElGamalCoder.getPrivateKey(keyMap);

System.err.println("公钥:\n"+Base64.encodeBase64String(publicKey));

System.err.println("私钥:\n"+Base64.encodeBase64String(privateKey));

}

/**

*校验

*@throws Exception

*/

@Test

public void test()throws Exception{

String inputStr="ElGamal加密";

byte[]data=inputStr.getBytes();

System.err.println("原文:\n"+inputStr);

byte[]encodedData=ElGamalCoder.encryptByPublicKey(data, publicKey);

System.err.println("加密后:\n"+Base64.encodeBase64String(encodedData));

byte[]decodedData=ElGamalCoder.decryptByPrivateKey(encodedData, privateKey);

String outputStr=new String(decodedData);

System.err.println("解密后:\n"+outputStr);

assertEquals(inputStr, outputStr);

}

}


在控制台中的输出信息中,我们可以得到相应的公钥和私钥,如下代码所示:


公钥:

MHcwUAYGKw4HAgEBMEYCIQCutlvZWBGgITJngn6hyMJ/VC/vt7K47W2p7QZdk+xpDwIhAKr3JJo1

jqbZp0YJSeBceSDLL7fJOUATmOzEyXhv0kRcAyMAAiA6HMzcFJSyF78uBXzemyHNFbXOFF0plX15

17p31YQqjQ==

私钥:

MHkCAQAwUAYGKw4HAgEBMEYCIQCutlvZWBGgITJngn6hyMJ/VC/vt7K47W2p7QZdk+xpDwIhAKr3

JJo1jqbZp0YJSeBceSDLL7fJOUATmOzEyXhv0kRcBCICIBecbEByJs28q7NH69zA2xDsjYbx9ihc

IZSZzKO8z/Dn


仔细观察,我们发现公钥和私钥的长度几乎是一致的。

观察控制台的输出信息,加密/解密信息如下代码所示:


原文:

ElGamal加密

加密后:

T92lluoBzFrAkly8I6b9PX9MuuGTiUAcGmn4Zw+iNYozA1BtX/RkhLTtPzDobJQKLAUV3fLN7Jeq

GZsgXC8gOA==

解密后:

ElGamal加密


ElGamal算法公钥和私钥长度几乎一致,基于Bouncy Castle加密组件的ElGamal算法实现仅遵循“公钥加密,私钥解密”的简单原则。