之前学习密码学,老师交给我们做一个试验项目,要求实现jpeg图片的加密、消息验证和数字签名,刚开始学的时候还是一头雾水,现在项目做的差不错了,可以展现给大家看一看了。
闲话不多说,主要看代码,边看代码边讲解思路。
首先说的是jpeg图片的格式问题,老师希望我们能实现的是部分加密(就是加密DC系数和AC系数中的一部分),但是由于需要解开jpeg图片的哈夫曼编码,太麻烦了,所以我主要用的还是数据的全加密,(这里不懂jpeg图片各个字段的朋友可以先去百度看一下),我所加密的是FFAD字段后面的数据。
说完jpeg图片问题之后,再次说的就是图片加密和计算HMAC值,因为计算好的MAC需要添加到消息的后面在进行加密,首先看一下加密以及计算MAC的流程图:
第一是读取jpeg图片这个就不多说。将jpeg图片的数据读到一个byte[]中,首先是对这个byte[]进行HMAC计算,计算这个图片的一个MAC值,将计算好的MAC值添加到数据段的后面,然后讲整体进行加密,加密之后传送给接收端,接受断收到消息之后,进行解密,取出解密之后数据的后20个字节(因为计算好的MAC值为20个字节),用前一部分数据在计算一个MAC值,如果两个MAC值相同说明消息没有被修改。
下面贴出DES加密的class和计算MAC值的class:
public class EncrypDES {
//KeyGenerator 提供对称密钥生成器的功能,支持各种算法
private KeyGenerator keygen;
//SecretKey 负责保存对称密钥
private static SecretKey deskey;
//Cipher负责完成加密或解密工作
private Cipher c;
//该字节数组负责保存加密的结果
private byte[] cipherByte;
public static SecretKey getSecretKey(){
return deskey;
}
public EncrypDES() throws NoSuchAlgorithmException, NoSuchPaddingException{
Security.addProvider(new com.sun.crypto.provider.SunJCE());
//实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)
keygen = KeyGenerator.getInstance("DES");
//生成密钥
deskey = keygen.generateKey();
//System.out.println("desket :"+deskey);
//生成Cipher对象,指定其支持的DES算法
c = Cipher.getInstance("DES"); ///ECB/NoPadding
}
//加密部分
public byte[] Encrytor(byte[] src,SecretKey key) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
// 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式
c.init(Cipher.ENCRYPT_MODE, key);
//byte[] src = str.getBytes();
// 加密,结果保存进cipherByte
cipherByte = c.doFinal(src);
return cipherByte;
}
//解密部分
public byte[] Decryptor(byte[] buff,SecretKey key) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
// 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式
c.init(Cipher.DECRYPT_MODE, key);
cipherByte = c.doFinal(buff);
return cipherByte;
}
}
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
public class HMAC {
/**
* 定义加密方式
*/
private final static String KEY_MAC = "HmacSHA1";
/**
* 初始化HMAC密钥
* @return
*/
public static SecretKey init() {
SecretKey key = null;
try {
KeyGenerator generator = KeyGenerator.getInstance(KEY_MAC);
key = generator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return key;
}
/**
* HMAC加密
*/
public static byte[] encryptHMAC(byte[] data, SecretKey key) {
byte[] bytes = null;
try {
Mac mac = Mac.getInstance(key.getAlgorithm());
mac.init(key);
bytes = mac.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
}
}
最后说一下进行数字签名。数字签名,顾名思义就是在消息上签上自己的名字,证明这个消息是由发送方发送的,为什么要证明是发送方发送的呢,这个牵扯就比较多了,举一个简单的例子就是,如果A和B有某些合作,由于B的操作失误导致整个合作项目失败了,B为了推卸责任可以伪造一个A给B发送的邮件(或者其他)来说是A让B这么做得,B就会减轻自己的责任。那么A为了证明自己没有发送过这个邮件,就需要验证一下,这个邮件是否有A的签名,如果有的话那就真的是A发送的,如果没有那么说明B在撒谎。
这只是一个简单的例子,下面我们来直接展示进行数字签名的代码过程:
public class MsgSignature {
//1.初始化获得密钥
public KeyPair init() throws NoSuchAlgorithmException{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
}
public RSAPrivateKey getPrivateKey(KeyPair keyPair){
return (RSAPrivateKey)keyPair.getPrivate();
}
public RSAPublicKey getPublicKey(KeyPair keyPair){
return (RSAPublicKey)keyPair.getPublic();
}
//进行签名操作
public byte[] RSAEncry(byte[] data,RSAPrivateKey rsaPrivateKey) throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException, SignatureException{
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(data);
byte[] result = signature.sign();
//System.out.println(result.length+" "+Hex.encodeHexString(result));
return result;
}
//验证签名
public boolean Check(byte[] data,byte[] result,RSAPublicKey rsaPublicKey) throws InvalidKeySpecException,
NoSuchAlgorithmException, InvalidKeyException, SignatureException{
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(result);
}
}
注:这个项目的源码地址:
http://www.oschina.net/code/snippet_2544978_56548