无论是哪一种加密算法,Key(密钥)都是不可或缺的。根据加密算法的不同,相应的key也是不一样的。在进行加密/解密的过程中,我们可能会涉及到这几个问题:Key的生成;Key的保存;根据保存的数据重新构造Key。下面将会对这几个问题一一讲解。
Key的分类
首先根据加密算法种类的不同,key可以分成两大类:- SecretKey-对称密钥:对应于对称加密算法(Symmetric algorithm),即加密密钥和解密密钥使用的是同一个密钥,代表算法有DES, AES 等。
- KeyPair-非对称密钥;对应于非对称加密算法(Asymmetric algorithm),即分为Public Key(公钥)和Private Key(私钥)。使用Public Key加密的信息,只有用Private Key才能够解密;反之,使用Private Key加密的信息,也只有用对应的Public Key才能解密。代表算法有DSA, RSA 等。
Key的生成
根据Key的种类不同,生成方式也不一样。我们主要使用两个类来进行Key的生成:- KeyGenerator类,用于生成SecretKey
- KeyPairGenerator类,用于生成KeyPair
KeyGenerator
使用KeyGenerator来生成SecretKey非常简单。首先,这是一个Engine类。好了那么我们就知道了它大概的用法:- 调用静态的getInstance方法,通过Algorithm和Provider获取一个KeyGenerator的实例
- 调用init方法对KeyGenerator的实例进行初始化:主要是指定一个Key Size和一个可选的随机数生成器(RNG),如果没有传入RNG,那么KeyGenerator将会使用默认的RNG——SecureRandom
- 调用generateKey方法来生成Secretkey
public static SecretKey generateDESKey() { try { KeyGenerator kg = KeyGenerator.getInstance("DES"); /** * 注意在SunJCE的默认实现中,要求DES Key Size必须是56, * 此处如果传入其它数值,将会抛出InvalidParameterException("Wrong keysize: must be equal to 56") * 详情请参见com.sun.crypto.provider.DESKeyGenerator.engineInit() 方法 */ kg.init(56); SecretKey key = kg.generateKey(); return key; } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } }每种加密算法对Key都有不同的要求,尤其是Key Size,根据算法的不同,需要不同的Key Size。下表来自于Java官方文档,里面列出了JDK默认实现的加密算法所需要的Key Size,以及默认值:
Algorithm Name | Default Keysize | Restrictions/Comments |
---|---|---|
AES | 128 | Keysize must be equal to 128, 192, or 256. |
ARCFOUR (RC4) | 128 | Keysize must range between 40 and 1024 (inclusive). |
Blowfish | 128 | Keysize must be a multiple of 8, ranging from 32 to 448 (inclusive). |
DES | 56 | Keysize must be equal to 56. |
DESede (Triple DES) | 168 |
Keysize must be equal to 112 or 168.
A keysize of 112 will generate a Triple DES key with 2 intermediate keys, and a keysize of 168 will generate a Triple DES key with 3 intermediate keys. Due to the "Meet-In-The-Middle" problem, even though 112 or 168 bits of key material are used, the effective keysize is 80 or 112 bits respectively. |
HmacMD5 | 512 | No keysize restriction. |
HmacSHA1 | 512 | No keysize restriction. |
HmacSHA256 | 256 | No keysize restriction. |
HmacSHA384 | 384 | No keysize restriction. |
HmacSHA512 | 512 | No keysize restriction. |
RC2 | 128 | Keysize must range between 40 and 1024 (inclusive). |
KeyPairGenerator
在说KeyPairGenerator之前,有必要先说一下KeyPair类。KeyPair类没有什么实际的功能,其实它的唯一作用就是创建了一个类,用于组织Public Key和Private Key。对应的key保存在这个类中对应的属性里面(publicKey & privateKey)。我们可以通过调用getPublic和getPrivate方法获取这相应的Key。
KeyPairGenerator的使用方法和KeyGenerator基本一样,因为它也是一个Engine类:
- 调用静态的getInstance方法,通过Algorithm和Provider获取一个KeyPairGenerator的实例
- 调用initialize方法对KeyPairGenerator的实例进行初始化:主要是指定一个Key Size和一个可选的随机数生成器(RNG),如果没有传入RNG,那么KeyPairGenerator将会使用默认的RNG——SecureRandom
- 调用genKeyPair方法或者generateKeyPair方法来生成KeyPair。(genKeyPair方法内部直接调用了generateKeyPair方法)
public static KeyPair generateRSAKeyPair() { try { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(2048); KeyPair keyPair = kpg.genKeyPair(); return keyPair; } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } }对于KeyPairGenerator来说,每种算法对于Key Size也有不同的要求。而且,即使是相同的算法,在不同的Provider实现中要求的Key Size也是不一样的。具体信息可以查看Java的官方文档(http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html)。
比如上面所用的RSA算法,在Java的安装文件中有两个Provider都提供了RSA算法的实现:SunRSASign Provider和SunMSCAPI Provider。在SunRSASign中要求Key Size在[512 bits, 65536 bits]之间;而在SumMSCAPI中要求RSA的Key Size在 [512 bits, 16384 bits]之间。所以,如果不清楚算法实现对Key Size的要求,最好的方法就是查看上面提到的Java官方文档,或者通过查看源码,自己发现要求的KeySize。
Key接口
在Java中,Key接口是所有具体Key的接口。无论是Symmetric Key还是Asymmetric Key都需要实现Key接口。Key接口是Key的一种不透明的表现形式(Opaque Representation),与之相对的是透明的表现形式KeySpec(Transparent Representation)。关于KeySpec的相关信息这里暂时不说。Key接口中只提供了非常有限的三个方法访问Key中封装的数据:/** * 返回能够使用这个Key的加密算法。 * 比如生成的是AES Key,那么这个方法将返回"AES"。 */ public String getAlgorithm(); /** * 返回Key的编码格式。 * Key的种类不同,保存的信息也不一样。 * 有的Key比较简单,只是保存了一些字节比如AES和DES, * 调用这些Key的getFormat方法将返回RAW,意思是原本的字节信息; * 另外有些key包含的信息就比较复杂了, * 比如RSA的Public Key,里面包含了modulus以及publicExponent, * 因此就需要把这些信息按照一定的编码方式来进行编码。 * 通过调用getFormat这个方法,就会返回使用的编码方式。 */ public String getFormat(); /** * 这个方法根据getFormat方法返回的编码方式,对Key进行编码,并返回编码后的结果。 * 这个方法可以用于将程序中生成的Key导出为byte 数组的形式。 * 利用导出的byte数组,我们可以完成许多功能。 * 比如将byte数组保存在文件中,这样就将Key持久化到了介质上; * 或者将byte数组通过网络传输给另一个太机器,实现Key的共享等等。 */ public byte[] getEncoded();介绍了Key接口中的方法,我们就可以明白为什么说“Key接口是Key的不透明表现形式(Opaque Representation)”了。因为通过Key接口我们不能得到Key中的各项信息,能够得到的仅仅是经过编码后的Key。比如我无法从Key中得到RSA Public Key里面的modulus信息。
下面是将key导出到文件的一个简单例子:
public static void saveKey(Key key, Path path) { //获取编码后的key byte[] keyData = key.getEncoded(); //将编码后的key转换成base64的字符串,方便在文件中查看 String keyStr = DatatypeConverter.printBase64Binary(keyData); try { Files.write(path, keyStr.getBytes("UTF-8"), StandardOpenOption.WRITE); } catch (IOException ex) { throw new RuntimeException(ex); } }
java语言示例
ReplyDeletejava以位为单位打印无符号整数