下面是阅读 Java Cryptography Architecture(JCA) Reference Guide的一些笔记
java 安全体系的结构
- Cryptographic Service Providers (加密服务提供者)
java.security.Provider 是所有安全服务提供者的基类,每一个CSP实例都包含了provider的名字和它实现了所有算法的名字。当需要一个特定算法的实例时,JCA就会查询provider,如果找到就返回相应的实例。
每个JDK安装了一个或者多个provider,需要更多的provider需要自己安装。
当在应用程序中使用JCA时,仅仅需要一个特定的对象和一个特定的算法/服务。例如MessageDigest对象中的MD5算法。这样便可以获得一个实例。对于provider的选取要么选择优先级最高的那个(如果没有指定具体的算法),要么选择指定的provider。
- provider的实现过程
每个引擎类提供的应用API都被实现相应SPI(Service Provider Interface)的类路由到provide的具体实现中。每一个引擎类都对应一个SPI。
调用过程:
假设需要使用AES算法。
.应用程序 调用 getInstance() 获得cipher 引擎类。
. cipher 引擎类要求JAC框架找到第一个满足AES的provider.
. JCA 框架查询所有的provider并且返回其中合适的一个provider.
. 然后provider找到相应的实现类,并创建实例和返回。
2. 密钥管理
keystore 可用来管理密钥和证书,它实现了java.security.KeyStore 类,应用程序可以向它请求数据用于认证、加密或者签名等。
An engine class provides the interface to a specific type of cryptographic service, independent of a particular cryptographic algorithm or provider.
static EngineClassName getInstance(String algorithm) throws NoSuchAlgorithmException
e.g.:
MessageDigest md = MessageDigest.getInstance(“MD5”); // MD5 对大小写不敏感
MessageDigest.getInstance(“SHA-1”);
产生安全的随机数
编写代码过程
1. create a signature object
Signature dsa = Signature.getInstance(“SHA1withDSA”);
2. 生成密钥对,并用私钥初始化上面的对象并对数据进行签名
PrivateKey priv = pair.getPrivate();
dsa.initSign(priv);
dsa.update(data);
byte[] sig = dsa.sign();
3. Verifying a Signature
PublicKey pub = pair.getPublic();
dsa.initVerify(pub);
dsa.update(data);
boolean verifies = dsa.verify(sig);
System.out.println(“signature verifies: ” + verifies);
另外如果我们拥有的不是公私钥对,而是生成公私钥对的参数,例如DSA private key: x (the private key), p (the prime), q (the sub-prime), and g (the base),那么必须先要生成公私钥。
DSAPrivateKeySpec dsaPrivKeySpec = new DSAPrivateKeySpec(x, p, q, g);
e.g.,:
Sign
*//私钥*
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privKey = keyFactory.generatePrivate(dsaPrivKeySpec);
Signature sig = Signature.getInstance("SHA1withDSA");
sig.initSign(privKey);
sig.update(someData);
byte[] signature = sig.sign();
*//公约*
DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(y, p, q, g);
byte[] encKey = pubKey.getEncoded(); //extract the (encoded) key bytes
Verify:
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Signature sig = Signature.getInstance("SHA1withDSA");
sig.initVerify(pubKey);
sig.update(data);
sig.verify(signature);
NOTE: 验证方可以获得p,q,g和公约。
DSAPublicKeySpec dsaPubKeySpec =
(DSAPublicKeySpec)keyFactory.getKeySpec(pubKey,
DSAPublicKeySpec.class);
dsaPubKeySpec.getX();
....
分组密码和流密码:分组密码一次处理一个数据块,如果数据不足,需要先填充再加密。填充方式可以为应用填充或者选择cipher的填充方式(PKCS5PADDING)。(我也不明白这是什么填充方法)。
流密码一次处理一个单元(如byte or bit),因此不用填充。
简单的分组密码(ECB),对于2个相同的数据加密后的数据是一样的,为了打破这种限制有另外的模式如CBC,CFB,OFB。 使用这些模式的时候,需要一个不需要保密的初始向量。另外AES、RSA的密钥长度是不固定的,而DES、3DES的密钥是固定的。(哇,这个都忘记了。)
1. Creating a Cipher Object
getInstance( transformation )
注意: A transformation is of the form:
“algorithm/mode/padding” or
“algorithm”
2. Initializing a Cipher Object
3. Encrypting and Decrypting Data
To encrypt or decrypt data in a single step, call one of the doFinal methods
To encrypt or decrypt data in multiple steps, call one of the update methods, a multiple-part operation must be terminated by one of the above doFinal methods.
一些重要的内容已经看完,下面是一些其他的内容。
密钥的包装,为了更加安全的传送密钥。
首先需要创建一个Cipher(WRAP_MODE), 然后调用wrap函数,
注意在获取密钥的时候,需要向unwrap的应用提供:算法的名字和包装密钥的类型(Cipher.SECRET_KEY, Cipher.PRIVATE_KEY, or Cipher.PUBLIC_KEY)。
为了解包密钥,同样需要定义个Cipher(UNWRAP_MODE),并且调用unwrap()方法来获得key。
可以通过调用getParameters来获取参数Cipher object 的参数,返回一个java.security.AlgorithmParameters 对象。
import javax.crypto.*;
import java.security.AlgorithmParameters;
// get cipher object for password-based encryption
Cipher c = Cipher.getInstance("PBEWithMD5AndDES");
// initialize cipher for encryption, without supplying
// any parameters. Here, "myKey" is assumed to refer
// to an already-generated key.
c.init(Cipher.ENCRYPT_MODE, myKey);
// encrypt some data and store away ciphertext
// for later decryption
byte[] cipherText = c.doFinal("This is just an example".getBytes());
// retrieve parameters generated by underlying cipher
// implementation
AlgorithmParameters algParams = c.getParameters();
// get parameter encoding and store it away
byte[] encodedAlgParams = algParams.getEncoded();
//利用上面的参数重新加密其他的内容
AlgorithmParameters algParams;
algParams = AlgorithmParameters.getInstance("PBEWithMD5AndDES");
// initialize with parameter encoding from above
algParams.init(encodedAlgParams);
// get cipher object for password-based encryption
Cipher c = Cipher.getInstance("PBEWithMD5AndDES");
// initialize cipher for decryption, using one of the
// init() methods that takes an AlgorithmParameters
// object, and pass it the algParams object from above
c.init(Cipher.DECRYPT_MODE, myKey, algParams);
Note:The following method in Cipher can be used to determine how big the output buffer should be:
public int getOutputSize(int inputLen)
example:
import java.security.*;
import javax.crypto.*;
/** * This program demonstrates how to generate a secret-key object for * HMAC-MD5, and initialize an HMAC-MD5 object with it. */
public class initMac {
public static void main(String[] args) throws Exception {
// Generate secret key for HMAC-MD5
KeyGenerator kg = KeyGenerator.getInstance("HmacMD5");
SecretKey sk = kg.generateKey();
// Get instance of Mac object implementing HMAC-MD5, and
// initialize it with the above secret key
Mac mac = Mac.getInstance("HmacMD5");
mac.init(sk);
byte[] result = mac.doFinal("Hi There".getBytes());
}
}