java des加密

Java加密技术


如基本的单向加密算法:

BASE64 严格地说,属于编码格式,而非加密算法
MD5(Message Digest algorithm 5,信息摘要算法)
SHA(Secure Hash Algorithm,安全散列算法)
HMAC(Hash Message Authentication Code,散列消息鉴别码)

    复杂的对称加密(DES、PBE)、非对称加密算法:

DES(Data Encryption Standard,数据加密算法)
PBE(Password-based encryption,基于密码验证)
RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman)
DH(Diffie-Hellman算法,密钥一致协议)
DSA(Digital Signature Algorithm,数字签名)
ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)


    本篇内容简要介绍BASE64、MD5、SHA、HMAC几种方法。
    MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。

BASE64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。




 

通过java代码实现如下:

Java代码  
/**
 * BASE64解密
 *  
 * @param key
 * @return
 * @throws Exception
 */  
public static byte[] decryptBASE64(String key) throws Exception {  
    return (new BASE64Decoder()).decodeBuffer(key);  
}  
 
/**
 * BASE64加密
 *  
 * @param key
 * @return
 * @throws Exception
 */  
public static String encryptBASE64(byte[] key) throws Exception {  
    return (new BASE64Encoder()).encodeBuffer(key);  
}  

主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。

MD5
MD5 -- message-digest algorithm 5 (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。




 

通过java代码实现如下:

Java代码  
/**
 * MD5加密
 *  
 * @param data
 * @return
 * @throws Exception
 */  
public static byte[] encryptMD5(byte[] data) throws Exception {  
 
    MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
    md5.update(data);  
 
    return md5.digest();  
 
}  


通常我们不直接使用上述MD5加密。通常将MD5产生的字节数组交给BASE64再加密一把,得到相应的字符串。

SHA
SHA(Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。




 

通过java代码实现如下:

Java代码  
    /**
     * SHA加密
     *  
     * @param data
     * @return
     * @throws Exception
     */  
    public static byte[] encryptSHA(byte[] data) throws Exception {  
 
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
        sha.update(data);  
 
        return sha.digest();  
 
    }  
}  


HMAC
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。




 

通过java代码实现如下:

Java代码  
/**
 * 初始化HMAC密钥
 *  
 * @return
 * @throws Exception
 */  
public static String initMacKey() throws Exception {  
    KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);  
 
    SecretKey secretKey = keyGenerator.generateKey();  
    return encryptBASE64(secretKey.getEncoded());  
}  
 
/**
 * HMAC加密
 *  
 * @param data
 * @param key
 * @return
 * @throws Exception
 */  
public static byte[] encryptHMAC(byte[] data, String key) throws Exception {  
 
    SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);  
    Mac mac = Mac.getInstance(secretKey.getAlgorithm());  
    mac.init(secretKey);  
 
    return mac.doFinal(data);  
 
}  


给出一个完整类,如下:

Java代码  
import java.security.MessageDigest;  
 
import javax.crypto.KeyGenerator;  
import javax.crypto.Mac;  
import javax.crypto.SecretKey;  
 
import sun.misc.BASE64Decoder;  
import sun.misc.BASE64Encoder;  
 
/**
 * 基础加密组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class Coder {  
    public static final String KEY_SHA = "SHA";  
    public static final String KEY_MD5 = "MD5";  
 
    /**
     * MAC算法可选以下多种算法
     *  
     *
 
  
     * HmacMD5  
     * HmacSHA1  
     * HmacSHA256  
     * HmacSHA384  
     * HmacSHA512
     *

     */  
    public static final String KEY_MAC = "HmacMD5";  
 
    /**
     * BASE64解密
     *  
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] decryptBASE64(String key) throws Exception {  
        return (new BASE64Decoder()).decodeBuffer(key);  
    }  
 
    /**
     * BASE64加密
     *  
     * @param key
     * @return
     * @throws Exception
     */  
    public static String encryptBASE64(byte[] key) throws Exception {  
        return (new BASE64Encoder()).encodeBuffer(key);  
    }  
 
    /**
     * MD5加密
     *  
     * @param data
     * @return
     * @throws Exception
     */  
    public static byte[] encryptMD5(byte[] data) throws Exception {  
 
        MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
        md5.update(data);  
 
        return md5.digest();  
 
    }  
 
    /**
     * SHA加密
     *  
     * @param data
     * @return
     * @throws Exception
     */  
    public static byte[] encryptSHA(byte[] data) throws Exception {  
 
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
        sha.update(data);  
 
        return sha.digest();  
 
    }  
 
    /**
     * 初始化HMAC密钥
     *  
     * @return
     * @throws Exception
     */  
    public static String initMacKey() throws Exception {  
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);  
 
        SecretKey secretKey = keyGenerator.generateKey();  
        return encryptBASE64(secretKey.getEncoded());  
    }  
 
    /**
     * HMAC加密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] encryptHMAC(byte[] data, String key) throws Exception {  
 
        SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);  
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());  
        mac.init(secretKey);  
 
        return mac.doFinal(data);  
 
    }  
}  


再给出一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class CoderTest {  
 
    @Test  
    public void test() throws Exception {  
        String inputStr = "简单加密";  
        System.err.println("原文:\n" + inputStr);  
 
        byte[] inputData = inputStr.getBytes();  
        String code = Coder.encryptBASE64(inputData);  
 
        System.err.println("BASE64加密后:\n" + code);  
 
        byte[] output = Coder.decryptBASE64(code);  
 
        String outputStr = new String(output);  
 
        System.err.println("BASE64解密后:\n" + outputStr);  
 
        // 验证BASE64加密解密一致性  
        assertEquals(inputStr, outputStr);  
 
        // 验证MD5对于同一内容加密是否一致  
        assertArrayEquals(Coder.encryptMD5(inputData), Coder  
                .encryptMD5(inputData));  
 
        // 验证SHA对于同一内容加密是否一致  
        assertArrayEquals(Coder.encryptSHA(inputData), Coder  
                .encryptSHA(inputData));  
 
        String key = Coder.initMacKey();  
        System.err.println("Mac密钥:\n" + key);  
 
        // 验证HMAC对于同一内容,同一密钥加密是否一致  
        assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(  
                inputData, key));  
 
        BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));  
        System.err.println("MD5:\n" + md5.toString(16));  
 
        BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));  
        System.err.println("SHA:\n" + sha.toString(32));  
 
        BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));  
        System.err.println("HMAC:\n" + mac.toString(16));  
    }  
}  


控制台输出:

Console代码  
原文:  
简单加密  
BASE64加密后:  
566A5Y2V5Yqg5a+G  
 
BASE64解密后:  
简单加密  
Mac密钥:  
uGxdHC+6ylRDaik++leFtGwiMbuYUJ6mqHWyhSgF4trVkVBBSQvY/a22xU8XT1RUemdCWW155Bke  
pBIpkd7QHg==  
 
MD5:  
-550b4d90349ad4629462113e7934de56  
SHA:  
91k9vo7p400cjkgfhjh0ia9qthsjagfn  
HMAC:  
2287d192387e95694bdbba2fa941009a  


注意
编译时,可能会看到如下提示:


引用
 


警告:sun.misc.BASE64Decoder 是 Sun 的专用 API,可能会在未来版本中删除

import sun.misc.BASE64Decoder;
               ^
警告:sun.misc.BASE64Encoder 是 Sun 的专用 API,可能会在未来版本中删除

import sun.misc.BASE64Encoder;
               ^


BASE64Encoder和BASE64Decoder是非官方JDK实现类。虽然可以在JDK里能找到并使用,但是在API里查不到。JRE 中 sun 和 com.sun 开头包的类都是未被文档化的,他们属于 java, javax 类库的基础,其中的实现大多数与底层平台有关,一般来说是不推荐使用的。


    BASE64的加密解密是双向的,可以求反解。
    MD5、SHA以及HMAC是单向加密,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。
    单向加密的用途主要是为了校验数据在传输过程中是否被修改。

 接下来我们介绍对称加密算法,最常用的莫过于DES数据加密算法。
DES
DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
  DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位。




 

通过java代码实现如下:Coder类见 Java加密技术(一)

Java代码  
import java.security.Key;  
import java.security.SecureRandom;  
 
import javax.crypto.Cipher;  
import javax.crypto.KeyGenerator;  
import javax.crypto.SecretKey;  
import javax.crypto.SecretKeyFactory;  
import javax.crypto.spec.DESKeySpec;  
 
 
/**
 * DES安全编码组件
 *  
 *
 
  
 * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)
 * DES                  key size must be equal to 56
 * DESede(TripleDES)    key size must be equal to 112 or 168
 * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
 * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
 * RC2                  key size must be between 40 and 1024 bits
 * RC4(ARCFOUR)         key size must be between 40 and 1024 bits
 * 具体内容 需要关注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html
 *

 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class DESCoder extends Coder {  
    /**
     * ALGORITHM 算法

     * 可替换为以下任意一种算法,同时key值的size相应改变。
     *  
     *
 
  
     * DES                  key size must be equal to 56
     * DESede(TripleDES)    key size must be equal to 112 or 168
     * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
     * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
     * RC2                  key size must be between 40 and 1024 bits
     * RC4(ARCFOUR)         key size must be between 40 and 1024 bits
     *

     *  
     * 在Key toKey(byte[] key)方法中使用下述代码
     * SecretKey secretKey = new SecretKeySpec(key, ALGORITHM); 替换
     *
     * DESKeySpec dks = new DESKeySpec(key);
     * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
     * SecretKey secretKey = keyFactory.generateSecret(dks);
     *

     */  
    public static final String ALGORITHM = "DES";  
 
    /**
     * 转换密钥

     *  
     * @param key
     * @return
     * @throws Exception
     */  
    private static Key toKey(byte[] key) throws Exception {  
        DESKeySpec dks = new DESKeySpec(key);  
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);  
        SecretKey secretKey = keyFactory.generateSecret(dks);  
 
        // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码  
        // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);  
 
        return secretKey;  
    }  
 
    /**
     * 解密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] decrypt(byte[] data, String key) throws Exception {  
        Key k = toKey(decryptBASE64(key));  
 
        Cipher cipher = Cipher.getInstance(ALGORITHM);  
        cipher.init(Cipher.DECRYPT_MODE, k);  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 加密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] encrypt(byte[] data, String key) throws Exception {  
        Key k = toKey(decryptBASE64(key));  
        Cipher cipher = Cipher.getInstance(ALGORITHM);  
        cipher.init(Cipher.ENCRYPT_MODE, k);  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 生成密钥
     *  
     * @return
     * @throws Exception
     */  
    public static String initKey() throws Exception {  
        return initKey(null);  
    }  
 
    /**
     * 生成密钥
     *  
     * @param seed
     * @return
     * @throws Exception
     */  
    public static String initKey(String seed) throws Exception {  
        SecureRandom secureRandom = null;  
 
        if (seed != null) {  
            secureRandom = new SecureRandom(decryptBASE64(seed));  
        } else {  
            secureRandom = new SecureRandom();  
        }  
 
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);  
        kg.init(secureRandom);  
 
        SecretKey secretKey = kg.generateKey();  
 
        return encryptBASE64(secretKey.getEncoded());  
    }  
}  

延续上一个类的实现,我们通过MD5以及SHA对字符串加密生成密钥,这是比较常见的密钥生成方式。
再给出一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class DESCoderTest {  
 
    @Test  
    public void test() throws Exception {  
        String inputStr = "DES";  
        String key = DESCoder.initKey();  
        System.err.println("原文:\t" + inputStr);  
 
        System.err.println("密钥:\t" + key);  
 
        byte[] inputData = inputStr.getBytes();  
        inputData = DESCoder.encrypt(inputData, key);  
 
        System.err.println("加密后:\t" + DESCoder.encryptBASE64(inputData));  
 
        byte[] outputData = DESCoder.decrypt(inputData, key);  
        String outputStr = new String(outputData);  
 
        System.err.println("解密后:\t" + outputStr);  
 
        assertEquals(inputStr, outputStr);  
    }  
}  

得到的输出内容如下:

Console代码  
原文: DES  
密钥: f3wEtRrV6q0=  
 
加密后:    C6qe9oNIzRY=  
 
解密后:    DES  

    由控制台得到的输出,我们能够比对加密、解密后结果一致。这是一种简单的加密解密方式,只有一个密钥。
    其实DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,只要换掉ALGORITHM换成对应的值,同时做一个代码替换SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密钥长度不同了。


Java代码  
/**
 * DES          key size must be equal to 56
 * DESede(TripleDES) key size must be equal to 112 or 168
 * AES          key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
 * Blowfish     key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
 * RC2          key size must be between 40 and 1024 bits
 * RC4(ARCFOUR) key size must be between 40 and 1024 bits
 **/
除了DES,我们还知道有DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)等多种对称加密方式,其实现方式大同小异,这里介绍对称加密的另一个算法——PBE

PBE
    PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式。




 

通过java代码实现如下:Coder类见 Java加密技术(一)

Java代码  
import java.security.Key;  
import java.util.Random;  
 
import javax.crypto.Cipher;  
import javax.crypto.SecretKey;  
import javax.crypto.SecretKeyFactory;  
import javax.crypto.spec.PBEKeySpec;  
import javax.crypto.spec.PBEParameterSpec;  
 
/**
 * PBE安全编码组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class PBECoder extends Coder {  
    /**
     * 支持以下任意一种算法
     *  
     *
 
  
     * PBEWithMD5AndDES  
     * PBEWithMD5AndTripleDES  
     * PBEWithSHA1AndDESede
     * PBEWithSHA1AndRC2_40
     *

     */  
    public static final String ALGORITHM = "PBEWITHMD5andDES";  
 
    /**
     * 盐初始化
     *  
     * @return
     * @throws Exception
     */  
    public static byte[] initSalt() throws Exception {  
        byte[] salt = new byte[8];  
        Random random = new Random();  
        random.nextBytes(salt);  
        return salt;  
    }  
 
    /**
     * 转换密钥

     *  
     * @param password
     * @return
     * @throws Exception
     */  
    private static Key toKey(String password) throws Exception {  
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());  
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);  
        SecretKey secretKey = keyFactory.generateSecret(keySpec);  
 
        return secretKey;  
    }  
 
    /**
     * 加密
     *  
     * @param data
     *            数据
     * @param password
     *            密码
     * @param salt
     *            盐
     * @return
     * @throws Exception
     */  
    public static byte[] encrypt(byte[] data, String password, byte[] salt)  
            throws Exception {  
 
        Key key = toKey(password);  
 
        PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);  
        Cipher cipher = Cipher.getInstance(ALGORITHM);  
        cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 解密
     *  
     * @param data
     *            数据
     * @param password
     *            密码
     * @param salt
     *            盐
     * @return
     * @throws Exception
     */  
    public static byte[] decrypt(byte[] data, String password, byte[] salt)  
            throws Exception {  
 
        Key key = toKey(password);  
 
        PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);  
        Cipher cipher = Cipher.getInstance(ALGORITHM);  
        cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);  
 
        return cipher.doFinal(data);  
 
    }  
}  


再给出一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class PBECoderTest {  
 
    @Test  
    public void test() throws Exception {  
        String inputStr = "abc";  
        System.err.println("原文: " + inputStr);  
        byte[] input = inputStr.getBytes();  
 
        String pwd = "efg";  
        System.err.println("密码: " + pwd);  
 
        byte[] salt = PBECoder.initSalt();  
 
        byte[] data = PBECoder.encrypt(input, pwd, salt);  
 
        System.err.println("加密后: " + PBECoder.encryptBASE64(data));  
 
        byte[] output = PBECoder.decrypt(data, pwd, salt);  
        String outputStr = new String(output);  
 
        System.err.println("解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
    }  
 
}  


控制台输出:

Console代码  
原文: abc  
密码: efg  
加密后: iCZ0uRtaAhE=  
 
解密后: abc  

    后续我们会介绍非对称加密算法,如RSA、DSA、DH、ECC等。

接下来我们介绍典型的非对称加密算法——RSA

RSA
    这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
    这种加密算法的特点主要是密钥的变化,上文我们看到DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA同时有两把钥匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。

流程分析:

甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。
甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。
乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密。


按如上步骤给出序列图,如下:









通过java代码实现如下:Coder类见 Java加密技术(一)

Java代码  
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.Signature;  
import java.security.interfaces.RSAPrivateKey;  
import java.security.interfaces.RSAPublicKey;  
import java.security.spec.PKCS8EncodedKeySpec;  
import java.security.spec.X509EncodedKeySpec;  
 
import java.util.HashMap;  
import java.util.Map;  
 
import javax.crypto.Cipher;  
 
/**
 * RSA安全编码组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class RSACoder extends Coder {  
    public static final String KEY_ALGORITHM = "RSA";  
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
 
    private static final String PUBLIC_KEY = "RSAPublicKey";  
    private static final String PRIVATE_KEY = "RSAPrivateKey";  
 
    /**
     * 用私钥对信息生成数字签名
     *  
     * @param data
     *            加密数据
     * @param privateKey
     *            私钥
     *  
     * @return
     * @throws Exception
     */  
    public static String sign(byte[] data, String privateKey) throws Exception {  
        // 解密由base64编码的私钥  
        byte[] keyBytes = decryptBASE64(privateKey);  
 
        // 构造PKCS8EncodedKeySpec对象  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
 
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
 
        // 取私钥匙对象  
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
 
        // 用私钥对信息生成数字签名  
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(priKey);  
        signature.update(data);  
 
        return encryptBASE64(signature.sign());  
    }  
 
    /**
     * 校验数字签名
     *  
     * @param data
     *            加密数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     *  
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     *  
     */  
    public static boolean verify(byte[] data, String publicKey, String sign)  
            throws Exception {  
 
        // 解密由base64编码的公钥  
        byte[] keyBytes = decryptBASE64(publicKey);  
 
        // 构造X509EncodedKeySpec对象  
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
 
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
 
        // 取公钥匙对象  
        PublicKey pubKey = keyFactory.generatePublic(keySpec);  
 
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(pubKey);  
        signature.update(data);  
 
        // 验证签名是否正常  
        return signature.verify(decryptBASE64(sign));  
    }  
 
    /**
     * 解密

     * 用私钥解密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String key)  
            throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
 
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        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
     * @throws Exception
     */  
    public static byte[] decryptByPublicKey(byte[] data, String key)  
            throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
 
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);  
 
        // 对数据解密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 加密

     * 用公钥加密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPublicKey(byte[] data, String key)  
            throws Exception {  
        // 对公钥解密  
        byte[] keyBytes = decryptBASE64(key);  
 
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        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);  
    }  
 
    /**
     * 加密

     * 用私钥加密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String key)  
            throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
 
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 取得私钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPrivateKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PRIVATE_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
 
    /**
     * 取得公钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPublicKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PUBLIC_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
 
    /**
     * 初始化密钥
     *  
     * @return
     * @throws Exception
     */  
    public static Map initKey() throws Exception {  
        KeyPairGenerator keyPairGen = KeyPairGenerator  
                .getInstance(KEY_ALGORITHM);  
        keyPairGen.initialize(1024);  
 
        KeyPair keyPair = keyPairGen.generateKeyPair();  
 
        // 公钥  
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
 
        // 私钥  
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
 
        Map keyMap = new HashMap(2);  
 
        keyMap.put(PUBLIC_KEY, publicKey);  
        keyMap.put(PRIVATE_KEY, privateKey);  
        return keyMap;  
    }  
}  

再给出一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
import org.junit.Before;  
import org.junit.Test;  
 
import java.util.Map;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class RSACoderTest {  
    private String publicKey;  
    private String privateKey;  
 
    @Before  
    public void setUp() throws Exception {  
        Map keyMap = RSACoder.initKey();  
 
        publicKey = RSACoder.getPublicKey(keyMap);  
        privateKey = RSACoder.getPrivateKey(keyMap);  
        System.err.println("公钥: \n\r" + publicKey);  
        System.err.println("私钥: \n\r" + privateKey);  
    }  
 
    @Test  
    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "abc";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);  
 
        byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,  
                privateKey);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
    }  
 
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);  
 
        byte[] decodedData = RSACoder  
                .decryptByPublicKey(encodedData, publicKey);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名  
        String sign = RSACoder.sign(encodedData, privateKey);  
        System.err.println("签名:\r" + sign);  
 
        // 验证签名  
        boolean status = RSACoder.verify(encodedData, publicKey, sign);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
 
    }  
 
}  

控制台输出:

Console代码  
公钥:   
 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYU/+I0+z1aBl5X6DUUOHQ7FZpmBSDbKTtx89J  
EcB64jFCkunELT8qiKly7fzEqD03g8ALlu5XvX+bBqHFy7YPJJP0ekE2X3wjUnh2NxlqpH3/B/xm  
1ZdSlCwDIkbijhBVDjA/bu5BObhZqQmDwIxlQInL9oVz+o6FbAZCyHBd7wIDAQAB  
 
私钥:   
 
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJhT/4jT7PVoGXlfoNRQ4dDsVmmY  
FINspO3Hz0kRwHriMUKS6cQtPyqIqXLt/MSoPTeDwAuW7le9f5sGocXLtg8kk/R6QTZffCNSeHY3  
GWqkff8H/GbVl1KULAMiRuKOEFUOMD9u7kE5uFmpCYPAjGVAicv2hXP6joVsBkLIcF3vAgMBAAEC  
gYBvZHWoZHmS2EZQqKqeuGr58eobG9hcZzWQoJ4nq/CarBAjw/VovUHE490uK3S9ht4FW7Yzg3LV  
/MB06Huifh6qf/X9NQA7SeZRRC8gnCQk6JuDIEVJOud5jU+9tyumJakDKodQ3Jf2zQtNr+5ZdEPl  
uwWgv9c4kmpjhAdyMuQmYQJBANn6pcgvyYaia52dnu+yBUsGkaFfwXkzFSExIbi0MXTkhEb/ER/D  
rLytukkUu5S5ecz/KBa8U4xIslZDYQbLz5ECQQCy5dutt7RsxN4+dxCWn0/1FrkWl2G329Ucewm3  
QU9CKu4D+7Kqdj+Ha3lXP8F0Etaaapi7+EfkRUpukn2ItZV/AkEAlk+I0iphxT1rCB0Q5CjWDY5S  
Df2B5JmdEG5Y2o0nLXwG2w44OLct/k2uD4cEcuITY5Dvi/4BftMCZwm/dnhEgQJACIktJSnJwxLV  
o9dchENPtlsCM9C/Sd2EWpqISSUlmfugZbJBwR5pQ5XeMUqKeXZYpP+HEBj1nS+tMH9u2/IGEwJA  
fL8mZiZXan/oBKrblAbplNcKWGRVD/3y65042PAEeghahlJMiYquV5DzZajuuT0wbJ5xQuZB01+X  
nfpFpBJ2dw==  
 
公钥加密——私钥解密  
加密前: abc  
 
解密后: abc  
公钥:   
 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdOj40yEB48XqWxmPILmJAc7UecIN7F32etSHF  
9rwbuEh3+iTPOGSxhoSQpOED0vOb0ZIMkBXZSgsxLaBSin2RZ09YKWRjtpCA0kDkiD11gj4tzTiM  
l9qq1kwSK7ZkGAgodEn3yIILVmQDuEImHOXFtulvJ71ka07u3LuwUNdB/wIDAQAB  
 
私钥:   
 
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN06PjTIQHjxepbGY8guYkBztR5w  
g3sXfZ61IcX2vBu4SHf6JM84ZLGGhJCk4QPS85vRkgyQFdlKCzEtoFKKfZFnT1gpZGO2kIDSQOSI  
PXWCPi3NOIyX2qrWTBIrtmQYCCh0SffIggtWZAO4QiYc5cW26W8nvWRrTu7cu7BQ10H/AgMBAAEC  
gYEAz2JWBizjI31bqhP4XiP9PuY5F3vqBW4T+L9cFbQiyumKJc58yzTWUAUGKIIn3enXLG7dNqGr  
mbJro4JeFIJ3CiVDpXR9+FluIgI4SXm7ioGKF2NOMA9LR5Fu82W+pLfpTN2y2SaLYWEDZyp53BxY  
j9gUxaxi1MQs+C1ZgDF2xmECQQDy70bQntbRfysP+ppCtd56YRnES1Tyekw0wryS2tr+ivQJl7JF  
gp5rPAOXpgrq36xHDwUspQ0sJ0vj0O7ywxr1AkEA6SAaLhrJJrYucC0jxwAhUYyaPN+aOsWymaRh  
9jA/Wc0wp29SbGTh5CcMuGpXm1g0M+FKW3dGiHgS3rVUKim4owJAbnxgapUzAgiiHxxMeDaavnHW  
9C2GrtjsO7qtZOTgYI/1uT8itvZW8lJTF+9OW8/qXE76fXl7ai9dFnl5kzMk2QJBALfHz/vCsArt  
mkRiwY6zApE4Z6tPl1V33ymSVovvUzHnOdD1SKQdD5t+UV/crb3QVi8ED0t2B0u0ZSPfDT/D7kMC  
QDpwdj9k2F5aokLHBHUNJPFDAp7a5QMaT64gv/d48ITJ68Co+v5WzLMpzJBYXK6PAtqIhxbuPEc2  
I2k1Afmrwyw=  
 
私钥加密——公钥解密  
加密前: sign  
 
解密后: sign  
私钥签名——公钥验证签名  
签名:  
ud1RsIwmSC1pN22I4IXteg1VD2FbiehKUfNxgVSHzvQNIK+d20FCkHCqh9djP3h94iWnIUY0ifU+  
mbJkhAl/i5krExOE0hknOnPMcEP+lZV1RbJI2zG2YooSp2XDleqrQk5e/QF2Mx0Zxt8Xsg7ucVpn  
i3wwbYWs9wSzIf0UjlM=  
 
状态:  
true  


    简要总结一下,使用公钥加密、私钥解密,完成了乙方到甲方的一次数据传递,通过私钥加密、公钥解密,同时通过私钥签名、公钥验证签名,完成了一次甲方到乙方的数据传递与验证,两次数据传递完成一整套的数据交互!

类似数字签名,数字信封是这样描述的:

数字信封
  数字信封用加密技术来保证只有特定的收信人才能阅读信的内容。
流程:
    信息发送方采用对称密钥来加密信息,然后再用接收方的公钥来加密此对称密钥(这部分称为数字信封),再将它和信息一起发送给接收方;接收方先用相应的私钥打开数字信封,得到对称密钥,然后使用对称密钥再解开信息。

接下来我们介绍DSA数字签名,非对称加密的另一种实现。
DSA
DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级!







通过java代码实现如下:Coder类见 Java加密技术(一)

Java代码  
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.Signature;  
import java.security.interfaces.DSAPrivateKey;  
import java.security.interfaces.DSAPublicKey;  
import java.security.spec.PKCS8EncodedKeySpec;  
import java.security.spec.X509EncodedKeySpec;  
import java.util.HashMap;  
import java.util.Map;  
 
/**
 * DSA安全编码组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class DSACoder extends Coder {  
 
    public static final String ALGORITHM = "DSA";  
 
    /**
     * 默认密钥字节数
     *  
     *
 
  
     * DSA  
     * Default Keysize 1024   
     * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
     *

     */  
    private static final int KEY_SIZE = 1024;  
 
    /**
     * 默认种子
     */  
    private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";  
 
    private static final String PUBLIC_KEY = "DSAPublicKey";  
    private static final String PRIVATE_KEY = "DSAPrivateKey";  
 
    /**
     * 用私钥对信息生成数字签名
     *  
     * @param data
     *            加密数据
     * @param privateKey
     *            私钥
     *  
     * @return
     * @throws Exception
     */  
    public static String sign(byte[] data, String privateKey) throws Exception {  
        // 解密由base64编码的私钥  
        byte[] keyBytes = decryptBASE64(privateKey);  
 
        // 构造PKCS8EncodedKeySpec对象  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
 
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);  
 
        // 取私钥匙对象  
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
 
        // 用私钥对信息生成数字签名  
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());  
        signature.initSign(priKey);  
        signature.update(data);  
 
        return encryptBASE64(signature.sign());  
    }  
 
    /**
     * 校验数字签名
     *  
     * @param data
     *            加密数据
     * @param publicKey
     *            公钥
     * @param sign
     *            数字签名
     *  
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     *  
     */  
    public static boolean verify(byte[] data, String publicKey, String sign)  
            throws Exception {  
 
        // 解密由base64编码的公钥  
        byte[] keyBytes = decryptBASE64(publicKey);  
 
        // 构造X509EncodedKeySpec对象  
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
 
        // ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);  
 
        // 取公钥匙对象  
        PublicKey pubKey = keyFactory.generatePublic(keySpec);  
 
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());  
        signature.initVerify(pubKey);  
        signature.update(data);  
 
        // 验证签名是否正常  
        return signature.verify(decryptBASE64(sign));  
    }  
 
    /**
     * 生成密钥
     *  
     * @param seed
     *            种子
     * @return 密钥对象
     * @throws Exception
     */  
    public static Map initKey(String seed) throws Exception {  
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);  
        // 初始化随机产生器  
        SecureRandom secureRandom = new SecureRandom();  
        secureRandom.setSeed(seed.getBytes());  
        keygen.initialize(KEY_SIZE, secureRandom);  
 
        KeyPair keys = keygen.genKeyPair();  
 
        DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();  
        DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();  
 
        Map map = new HashMap(2);  
        map.put(PUBLIC_KEY, publicKey);  
        map.put(PRIVATE_KEY, privateKey);  
 
        return map;  
    }  
 
    /**
     * 默认生成密钥
     *  
     * @return 密钥对象
     * @throws Exception
     */  
    public static Map initKey() throws Exception {  
        return initKey(DEFAULT_SEED);  
    }  
 
    /**
     * 取得私钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPrivateKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PRIVATE_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
 
    /**
     * 取得公钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPublicKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PUBLIC_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
}  


再给出一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
import java.util.Map;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class DSACoderTest {  
 
    @Test  
    public void test() throws Exception {  
        String inputStr = "abc";  
        byte[] data = inputStr.getBytes();  
 
        // 构建密钥  
        Map keyMap = DSACoder.initKey();  
 
        // 获得密钥  
        String publicKey = DSACoder.getPublicKey(keyMap);  
        String privateKey = DSACoder.getPrivateKey(keyMap);  
 
        System.err.println("公钥:\r" + publicKey);  
        System.err.println("私钥:\r" + privateKey);  
 
        // 产生签名  
        String sign = DSACoder.sign(data, privateKey);  
        System.err.println("签名:\r" + sign);  
 
        // 验证签名  
        boolean status = DSACoder.verify(data, publicKey, sign);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
 
    }  
 
}  

控制台输出:

Console代码  
公钥:  
MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp  
RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn  
xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuE  
C/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJ  
FnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo  
g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAIu4RUlcQLp49PI0MrbssOY+3uySVnp0TULSv  
5T4VaHoKzsLHgGTrwOvsGA+V3yCNl2WDu3D84bSLF7liTWgOj+SMOEaPk4VyRTlLXZWGPsf1Mfd9  
21XAbMeVyKDSHHVGbMjBScajf3bXooYQMlyoHiOt/WrCo+mv7efstMM0PGo=  
 
私钥:  
MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2  
USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4  
O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC  
ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB  
gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR  
kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIegLUtmm2oQKQJTOiLugHTSjl/q  
 
签名:  
MC0CFQCMg0J/uZmF8GuRpr3TNq48w60nDwIUJCyYNah+HtbU6NcQfy8Ac6LeLQs=  
 
状态:  
true  


注意状态为true,就验证成功!

ECC
ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。
    当我开始整理《Java加密技术(二)》的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的资料,无论是官方还是非官方的解释,最终只有一种答案——ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。 如果想要获得ECC算法实现,需要调用硬件完成加密/解密(ECC算法相当耗费资源,如果单纯使用CPU进行加密/解密,效率低下),涉及到Java Card领域,PKCS#11。 其实,PKCS#11配置很简单,但缺乏硬件设备,无法尝试!

    尽管如此,我照旧提供相应的Java实现代码,以供大家参考。

通过java代码实现如下:Coder类见 Java加密技术(一)

Java代码  
import java.math.BigInteger;  
import java.security.Key;  
import java.security.KeyFactory;  
import java.security.interfaces.ECPrivateKey;  
import java.security.interfaces.ECPublicKey;  
import java.security.spec.ECFieldF2m;  
import java.security.spec.ECParameterSpec;  
import java.security.spec.ECPoint;  
import java.security.spec.ECPrivateKeySpec;  
import java.security.spec.ECPublicKeySpec;  
import java.security.spec.EllipticCurve;  
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.NullCipher;  
 
import sun.security.ec.ECKeyFactory;  
import sun.security.ec.ECPrivateKeyImpl;  
import sun.security.ec.ECPublicKeyImpl;  
 
/**
 * ECC安全编码组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class ECCCoder extends Coder {  
 
    public static final String ALGORITHM = "EC";  
    private static final String PUBLIC_KEY = "ECCPublicKey";  
    private static final String PRIVATE_KEY = "ECCPrivateKey";  
 
    /**
     * 解密

     * 用私钥解密
     *  
     * @param data
     * @param key
     * @return
     * @throws Exception
     */  
    public static byte[] decrypt(byte[] data, String key) throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
 
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = ECKeyFactory.INSTANCE;  
 
        ECPrivateKey priKey = (ECPrivateKey) keyFactory  
                .generatePrivate(pkcs8KeySpec);  
 
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(),  
                priKey.getParams());  
 
        // 对数据解密  
        // TODO Chipher不支持EC算法 未能实现  
        Cipher cipher = new NullCipher();  
        // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());  
        cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 加密

     * 用公钥加密
     *  
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */  
    public static byte[] encrypt(byte[] data, String privateKey)  
            throws Exception {  
        // 对公钥解密  
        byte[] keyBytes = decryptBASE64(privateKey);  
 
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = ECKeyFactory.INSTANCE;  
 
        ECPublicKey pubKey = (ECPublicKey) keyFactory  
                .generatePublic(x509KeySpec);  
 
        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(),  
                pubKey.getParams());  
 
        // 对数据加密  
        // TODO Chipher不支持EC算法 未能实现  
        Cipher cipher = new NullCipher();  
        // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());  
        cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());  
 
        return cipher.doFinal(data);  
    }  
 
    /**
     * 取得私钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPrivateKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PRIVATE_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
 
    /**
     * 取得公钥
     *  
     * @param keyMap
     * @return
     * @throws Exception
     */  
    public static String getPublicKey(Map keyMap)  
            throws Exception {  
        Key key = (Key) keyMap.get(PUBLIC_KEY);  
 
        return encryptBASE64(key.getEncoded());  
    }  
 
    /**
     * 初始化密钥
     *  
     * @return
     * @throws Exception
     */  
    public static Map initKey() throws Exception {  
        BigInteger x1 = new BigInteger(  
                "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);  
        BigInteger x2 = new BigInteger(  
                "289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);  
 
        ECPoint g = new ECPoint(x1, x2);  
 
        // the order of generator  
        BigInteger n = new BigInteger(  
                "5846006549323611672814741753598448348329118574063", 10);  
        // the cofactor  
        int h = 2;  
        int m = 163;  
        int[] ks = { 7, 6, 3 };  
        ECFieldF2m ecField = new ECFieldF2m(m, ks);  
        // y^2+xy=x^3+x^2+1  
        BigInteger a = new BigInteger("1", 2);  
        BigInteger b = new BigInteger("1", 2);  
 
        EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);  
 
        ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,  
                n, h);  
        // 公钥  
        ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);  
 
        BigInteger s = new BigInteger(  
                "1234006549323611672814741753598448348329118574063", 10);  
        // 私钥  
        ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);  
 
        Map keyMap = new HashMap(2);  
 
        keyMap.put(PUBLIC_KEY, publicKey);  
        keyMap.put(PRIVATE_KEY, privateKey);  
 
        return keyMap;  
    }  
 
}  


    请注意上述代码中的TODO内容,再次提醒注意,Chipher不支持EC算法 ,以上代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。为了确保程序能够正常执行,我们使用了NullCipher类,验证程序。

照旧提供一个测试类:

Java代码  
import static org.junit.Assert.*;  
 
import java.math.BigInteger;  
import java.security.spec.ECFieldF2m;  
import java.security.spec.ECParameterSpec;  
import java.security.spec.ECPoint;  
import java.security.spec.ECPrivateKeySpec;  
import java.security.spec.ECPublicKeySpec;  
import java.security.spec.EllipticCurve;  
import java.util.Map;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class ECCCoderTest {  
 
    @Test  
    public void test() throws Exception {  
        String inputStr = "abc";  
        byte[] data = inputStr.getBytes();  
 
        Map keyMap = ECCCoder.initKey();  
 
        String publicKey = ECCCoder.getPublicKey(keyMap);  
        String privateKey = ECCCoder.getPrivateKey(keyMap);  
        System.err.println("公钥: \n" + publicKey);  
        System.err.println("私钥: \n" + privateKey);  
 
        byte[] encodedData = ECCCoder.encrypt(data, publicKey);  
 
        byte[] decodedData = ECCCoder.decrypt(encodedData, privateKey);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
    }  
}  


控制台输出:

Console代码  
公钥:   
MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9YMh8u  
gAU21TjM2qPZ  
 
私钥:   
MDICAQAwEAYHKoZIzj0CAQYFK4EEAAEEGzAZAgEBBBTYJsR3BN7TFw7JHcAHFkwNmfil7w==  
 
加密前: abc  
 
解密后: abc  
Java加密技术(八)——数字证书

博客分类:
 
Java/Security
Javakeystorekeytool数字证书
    本篇的主要内容为Java证书体系的实现。

请大家在阅读本篇内容时先阅读 Java加密技术(四),预先了解RSA加密算法。

在构建Java代码实现前,我们需要完成证书的制作。
1.生成keyStroe文件
在命令行下执行以下命令:
Shell代码  
keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore  


其中
-genkey表示生成密钥
-validity指定证书有效期,这里是36000天
-alias指定别名,这里是www.zlex.org
-keyalg指定算法,这里是RSA
-keystore指定存储位置,这里是d:\zlex.keystore

在这里我使用的密码为 123456

控制台输出:
Console代码  
输入keystore密码:  
再次输入新密码:  
您的名字与姓氏是什么?  
  [Unknown]:  www.zlex.org  
您的组织单位名称是什么?  
  [Unknown]:  zlex  
您的组织名称是什么?  
  [Unknown]:  zlex  
您所在的城市或区域名称是什么?  
  [Unknown]:  BJ  
您所在的州或省份名称是什么?  
  [Unknown]:  BJ  
该单位的两字母国家代码是什么  
  [Unknown]:  CN  
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?  
  [否]:  Y  
 
输入的主密码  
        (如果和 keystore 密码相同,按回车):  
再次输入新密码:  

这时,在D盘下会生成一个zlex.keystore的文件。

2.生成自签名证书
光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。
导出证书:
Shell代码  
keytool -export -keystore d:\zlex.keystore -alias www.zlex.org -file d:\zlex.cer -rfc  


其中
-export指定为导出操作
-keystore指定keystore文件
-alias指定导出keystore文件中的别名
-file指向导出路径
-rfc以文本格式输出,也就是以BASE64编码输出
这里的密码是 123456

控制台输出:
Console代码  
输入keystore密码:  
保存在文件中的认证  


当然,使用方是需要导入证书的!
可以通过自签名证书完成CAS单点登录系统的构建!

Ok,准备工作完成,开始Java实现!

通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码  
import java.io.FileInputStream;  
import java.security.KeyStore;  
import java.security.PrivateKey;  
import java.security.PublicKey;  
import java.security.Signature;  
import java.security.cert.Certificate;  
import java.security.cert.CertificateFactory;  
import java.security.cert.X509Certificate;  
import java.util.Date;  
 
import javax.crypto.Cipher;  
 
/**
 * 证书组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class CertificateCoder extends Coder {  
 
 
    /**
     * Java密钥库(Java Key Store,JKS)KEY_STORE
     */  
    public static final String KEY_STORE = "JKS";  
 
    public static final String X509 = "X.509";  
 
    /**
     * 由KeyStore获得私钥
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    private static PrivateKey getPrivateKey(String keyStorePath, String alias,  
            String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());  
        return key;  
    }  
 
    /**
     * 由Certificate获得公钥
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static PublicKey getPublicKey(String certificatePath)  
            throws Exception {  
        Certificate certificate = getCertificate(certificatePath);  
        PublicKey key = certificate.getPublicKey();  
        return key;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String certificatePath)  
            throws Exception {  
        CertificateFactory certificateFactory = CertificateFactory  
                .getInstance(X509);  
        FileInputStream in = new FileInputStream(certificatePath);  
 
        Certificate certificate = certificateFactory.generateCertificate(in);  
        in.close();  
 
        return certificate;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String keyStorePath,  
            String alias, String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        Certificate certificate = ks.getCertificate(alias);  
 
        return certificate;  
    }  
 
    /**
     * 获得KeyStore
     *  
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */  
    private static KeyStore getKeyStore(String keyStorePath, String password)  
            throws Exception {  
        FileInputStream is = new FileInputStream(keyStorePath);  
        KeyStore ks = KeyStore.getInstance(KEY_STORE);  
        ks.load(is, password.toCharArray());  
        is.close();  
        return ks;  
    }  
 
    /**
     * 私钥加密
     *  
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String password) throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 私钥解密
     *  
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String password) throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥加密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
 
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥解密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(String certificatePath) {  
        return verifyCertificate(new Date(), certificatePath);  
    }  
 
    /**
     * 验证Certificate是否过期或无效
     *  
     * @param date
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(Date date, String certificatePath) {  
        boolean status = true;  
        try {  
            // 取得证书  
            Certificate certificate = getCertificate(certificatePath);  
            // 验证证书是否过期或无效  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证证书是否过期或无效
     *  
     * @param date
     * @param certificate
     * @return
     */  
    private static boolean verifyCertificate(Date date, Certificate certificate) {  
        boolean status = true;  
        try {  
            X509Certificate x509Certificate = (X509Certificate) certificate;  
            x509Certificate.checkValidity(date);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 签名
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     *  
     * @return
     * @throws Exception
     */  
    public static String sign(byte[] sign, String keyStorePath, String alias,  
            String password) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(  
                keyStorePath, alias, password);  
        // 获取私钥  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        // 取得私钥  
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password  
                .toCharArray());  
 
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);  
        return encryptBASE64(signature.sign());  
    }  
 
    /**
     * 验证签名
     *  
     * @param data
     * @param sign
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static boolean verify(byte[] data, String sign,  
            String certificatePath) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
 
        return signature.verify(decryptBASE64(sign));  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */  
    public static boolean verifyCertificate(Date date, String keyStorePath,  
            String alias, String password) {  
        boolean status = true;  
        try {  
            Certificate certificate = getCertificate(keyStorePath, alias,  
                    password);  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */  
    public static boolean verifyCertificate(String keyStorePath, String alias,  
            String password) {  
        return verifyCertificate(new Date(), keyStorePath, alias, password);  
    }  
}  


再给出一个测试类:
Java代码  
import static org.junit.Assert.*;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class CertificateCoderTest {  
    private String password = "123456";  
    private String alias = "www.zlex.org";  
    private String certificatePath = "d:/zlex.cer";  
    private String keyStorePath = "d:/zlex.keystore";  
 
    @Test  
    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "Ceritifcate";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
                certificatePath);  
 
        byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
                keyStorePath, alias, password);  
        String outputStr = new String(decrypt);  
 
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
 
        // 验证数据一致  
        assertArrayEquals(data, decrypt);  
 
        // 验证证书有效  
        assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
 
    }  
 
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
 
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
                keyStorePath, alias, password);  
 
        byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
                certificatePath);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名  
        String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
                password);  
        System.err.println("签名:\r" + sign);  
 
        // 验证签名  
        boolean status = CertificateCoder.verify(encodedData, sign,  
                certificatePath);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
 
    }  
}  


控制台输出:
Console代码  
公钥加密——私钥解密  
加密前: Ceritificate  
 
解密后: Ceritificate  
 
私钥加密——公钥解密  
加密前: sign  
 
解密后: sign  
私钥签名——公钥验证签名  
签名:  
pqBn5m6PJlfOjH0A6U2o2mUmBsfgyEY1NWCbiyA/I5Gc3gaVNVIdj/zkGNZRqTjhf3+J9a9z9EI7  
6F2eWYd7punHx5oh6hfNgcKbVb52EfItl4QEN+djbXiPynn07+Lbg1NOjULnpEd6ZhLP1YwrEAuM  
OfvX0e7/wplxLbySaKQ=  
 
状态:  
true  


由此完成了证书验证体系!

同样,我们可以对代码做签名——代码签名!
通过工具JarSigner可以完成代码签名。
这里我们对tools.jar做代码签名,命令如下:
Shell代码  
jarsigner -storetype jks -keystore zlex.keystore -verbose tools.jar www.zlex.org  

控制台输出:
Console代码  
输入密钥库的口令短语:  
 正在更新: META-INF/WWW_ZLEX.SF  
 正在更新: META-INF/WWW_ZLEX.RSA  
  正在签名: org/zlex/security/Security.class  
  正在签名: org/zlex/tool/Main$1.class  
  正在签名: org/zlex/tool/Main$2.class  
  正在签名: org/zlex/tool/Main.class  
 
警告:  
签名者证书将在六个月内过期。  


此时,我们可以对签名后的jar做验证!
验证tools.jar,命令如下:
Shell代码  
jarsigner -verify -verbose -certs tools.jar  

控制台输出:
Console代码  
         402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF  
         532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.SF  
         889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.RSA  
sm       590 Wed Dec 10 13:03:42 CST 2008 org/zlex/security/Security.class  
 
      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
      [证书将在 09-9-18 下午3:27 到期]  
 
sm       705 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$1.class  
 
      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
      [证书将在 09-9-18 下午3:27 到期]  
 
sm       779 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$2.class  
 
      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
      [证书将在 09-9-18 下午3:27 到期]  
 
sm     12672 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main.class  
 
      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
      [证书将在 09-9-18 下午3:27 到期]  
 
 
  s = 已验证签名  
  m = 在清单中列出条目  
  k = 在密钥库中至少找到了一个证书  
  i = 在身份作用域内至少找到了一个证书  
 
jar 已验证。  
 
警告:  
此 jar 包含签名者证书将在六个月内过期的条目。  


代码签名认证的用途主要是对发布的软件做验证,支持 Sun Java .jar (Java Applet) 文件(J2SE)和 J2ME MIDlet Suite 文件。

Java加密技术(九)——初探SSL

博客分类:
 
Java/Security
JavaTomcatsslkeystorekeytool
    在Java加密技术(八)中,我们模拟了一个基于RSA非对称加密网络的安全通信。现在我们深度了解一下现有的安全网络通信——SSL。
    我们需要构建一个由CA机构签发的有效证书,这里我们使用上文中生成的自签名证书zlex.cer
    这里,我们将证书导入到我们的密钥库。

Shell代码  
keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore  


其中
-import表示导入
-alias指定别名,这里是www.zlex.org
-file指定算法,这里是d:/zlex.cer
-keystore指定存储位置,这里是d:/zlex.keystore
在这里我使用的密码为654321

控制台输出:
Console代码  
输入keystore密码:  
再次输入新密码:  
所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN  
序列号:4a1e48df  
有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009  
证书指纹:  
         MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A  
         SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4  
         签名算法名称:SHA1withRSA  
         版本: 3  
信任这个认证? [否]:  y  
认证已添加至keystore中  


OK,最复杂的准备工作已经完成。
接下来我们将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。
现在,配置tomcat。先将zlex.keystore拷贝到tomcat的conf目录下,然后配置server.xml。将如下内容加入配置文件
Xml代码  
    SSLEnabled="true"  
    URIEncoding="UTF-8"  
    clientAuth="false"  
    keystoreFile="conf/zlex.keystore"  
    keystorePass="123456"  
    maxThreads="150"  
    port="443"  
    protocol="HTTP/1.1"  
    scheme="https"  
    secure="true"  
    sslProtocol="TLS" />  

注意clientAuth="false"测试阶段,置为false,正式使用时建议使用true。现在启动tomcat,访问https://www.zlex.org/。
显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器(IE,其他浏览器对于域名www.zlex.org不支持本地方式访问),访问https://www.zlex.org/,你会看到地址栏中会有个小锁,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。
这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要!

接着上篇内容,给出如下代码实现:
Java代码  
import java.io.FileInputStream;  
import java.security.KeyStore;  
import java.security.PrivateKey;  
import java.security.PublicKey;  
import java.security.Signature;  
import java.security.cert.Certificate;  
import java.security.cert.CertificateFactory;  
import java.security.cert.X509Certificate;  
import java.util.Date;  
 
import javax.crypto.Cipher;  
import javax.net.ssl.HttpsURLConnection;  
import javax.net.ssl.KeyManagerFactory;  
import javax.net.ssl.SSLContext;  
import javax.net.ssl.SSLSocketFactory;  
import javax.net.ssl.TrustManagerFactory;  
 
/**
 * 证书组件
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public abstract class CertificateCoder extends Coder {  
 
    /**
     * Java密钥库(Java Key Store,JKS)KEY_STORE
     */  
    public static final String KEY_STORE = "JKS";  
 
    public static final String X509 = "X.509";  
    public static final String SunX509 = "SunX509";  
    public static final String SSL = "SSL";  
 
    /**
     * 由KeyStore获得私钥
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    private static PrivateKey getPrivateKey(String keyStorePath, String alias,  
            String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());  
        return key;  
    }  
 
    /**
     * 由Certificate获得公钥
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static PublicKey getPublicKey(String certificatePath)  
            throws Exception {  
        Certificate certificate = getCertificate(certificatePath);  
        PublicKey key = certificate.getPublicKey();  
        return key;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String certificatePath)  
            throws Exception {  
        CertificateFactory certificateFactory = CertificateFactory  
                .getInstance(X509);  
        FileInputStream in = new FileInputStream(certificatePath);  
 
        Certificate certificate = certificateFactory.generateCertificate(in);  
        in.close();  
 
        return certificate;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String keyStorePath,  
            String alias, String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        Certificate certificate = ks.getCertificate(alias);  
 
        return certificate;  
    }  
 
    /**
     * 获得KeyStore
     *  
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */  
    private static KeyStore getKeyStore(String keyStorePath, String password)  
            throws Exception {  
        FileInputStream is = new FileInputStream(keyStorePath);  
        KeyStore ks = KeyStore.getInstance(KEY_STORE);  
        ks.load(is, password.toCharArray());  
        is.close();  
        return ks;  
    }  
 
    /**
     * 私钥加密
     *  
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String password) throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 私钥解密
     *  
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String password) throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥加密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
 
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥解密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(String certificatePath) {  
        return verifyCertificate(new Date(), certificatePath);  
    }  
 
    /**
     * 验证Certificate是否过期或无效
     *  
     * @param date
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(Date date, String certificatePath) {  
        boolean status = true;  
        try {  
            // 取得证书  
            Certificate certificate = getCertificate(certificatePath);  
            // 验证证书是否过期或无效  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证证书是否过期或无效
     *  
     * @param date
     * @param certificate
     * @return
     */  
    private static boolean verifyCertificate(Date date, Certificate certificate) {  
        boolean status = true;  
        try {  
            X509Certificate x509Certificate = (X509Certificate) certificate;  
            x509Certificate.checkValidity(date);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 签名
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     *  
     * @return
     * @throws Exception
     */  
    public static String sign(byte[] sign, String keyStorePath, String alias,  
            String password) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(  
                keyStorePath, alias, password);  
        // 获取私钥  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        // 取得私钥  
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password  
                .toCharArray());  
 
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);  
        return encryptBASE64(signature.sign());  
    }  
 
    /**
     * 验证签名
     *  
     * @param data
     * @param sign
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static boolean verify(byte[] data, String sign,  
            String certificatePath) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
 
        return signature.verify(decryptBASE64(sign));  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */  
    public static boolean verifyCertificate(Date date, String keyStorePath,  
            String alias, String password) {  
        boolean status = true;  
        try {  
            Certificate certificate = getCertificate(keyStorePath, alias,  
                    password);  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */  
    public static boolean verifyCertificate(String keyStorePath, String alias,  
            String password) {  
        return verifyCertificate(new Date(), keyStorePath, alias, password);  
    }  
 
    /**
     * 获得SSLSocektFactory
     *  
     * @param password
     *            密码
     * @param keyStorePath
     *            密钥库路径
     *  
     * @param trustKeyStorePath
     *            信任库路径
     * @return
     * @throws Exception
     */  
    private static SSLSocketFactory getSSLSocketFactory(String password,  
            String keyStorePath, String trustKeyStorePath) throws Exception {  
        // 初始化密钥库  
        KeyManagerFactory keyManagerFactory = KeyManagerFactory  
                .getInstance(SunX509);  
        KeyStore keyStore = getKeyStore(keyStorePath, password);  
        keyManagerFactory.init(keyStore, password.toCharArray());  
 
        // 初始化信任库  
        TrustManagerFactory trustManagerFactory = TrustManagerFactory  
                .getInstance(SunX509);  
        KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);  
        trustManagerFactory.init(trustkeyStore);  
 
        // 初始化SSL上下文  
        SSLContext ctx = SSLContext.getInstance(SSL);  
        ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory  
                .getTrustManagers(), null);  
        SSLSocketFactory sf = ctx.getSocketFactory();  
 
        return sf;  
    }  
 
    /**
     * 为HttpsURLConnection配置SSLSocketFactory
     *  
     * @param conn
     *            HttpsURLConnection
     * @param password
     *            密码
     * @param keyStorePath
     *            密钥库路径
     *  
     * @param trustKeyStorePath
     *            信任库路径
     * @throws Exception
     */  
    public static void configSSLSocketFactory(HttpsURLConnection conn,  
            String password, String keyStorePath, String trustKeyStorePath)  
            throws Exception {  
        conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,  
                trustKeyStorePath));  
    }  
}  


增加了configSSLSocketFactory方法供外界调用,该方法为HttpsURLConnection配置了SSLSocketFactory。当HttpsURLConnection配置了SSLSocketFactory后,我们就可以通过HttpsURLConnection的getInputStream、getOutputStream,像往常使用HttpURLConnection做操作了。尤其要说明一点,未配置SSLSocketFactory前,HttpsURLConnection的getContentLength()获得值永远都是-1。

给出相应测试类:
Java代码  
import static org.junit.Assert.*;  
 
import java.io.DataInputStream;  
import java.io.InputStream;  
import java.net.URL;  
 
import javax.net.ssl.HttpsURLConnection;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class CertificateCoderTest {  
    private String password = "123456";  
    private String alias = "www.zlex.org";  
    private String certificatePath = "d:/zlex.cer";  
    private String keyStorePath = "d:/zlex.keystore";  
    private String clientKeyStorePath = "d:/zlex-client.keystore";  
    private String clientPassword = "654321";  
 
    @Test  
    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "Ceritifcate";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
                certificatePath);  
 
        byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
                keyStorePath, alias, password);  
        String outputStr = new String(decrypt);  
 
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
 
        // 验证数据一致  
        assertArrayEquals(data, decrypt);  
 
        // 验证证书有效  
        assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
 
    }  
 
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
 
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
                keyStorePath, alias, password);  
 
        byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
                certificatePath);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名  
        String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
                password);  
        System.err.println("签名:\r" + sign);  
 
        // 验证签名  
        boolean status = CertificateCoder.verify(encodedData, sign,  
                certificatePath);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
 
    }  
 
    @Test  
    public void testHttps() throws Exception {  
        URL url = new URL("https://www.zlex.org/examples/");  
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();  
 
        conn.setDoInput(true);  
        conn.setDoOutput(true);  
 
        CertificateCoder.configSSLSocketFactory(conn, clientPassword,  
                clientKeyStorePath, clientKeyStorePath);  
 
        InputStream is = conn.getInputStream();  
 
        int length = conn.getContentLength();  
 
        DataInputStream dis = new DataInputStream(is);  
        byte[] data = new byte[length];  
        dis.readFully(data);  
 
        dis.close();  
        System.err.println(new String(data));  
        conn.disconnect();  
    }  
}  

注意testHttps方法,几乎和我们往常做HTTP访问没有差别,我们来看控制台输出:
Console代码  
 
 
Apache Tomcat Examples  
 
 
 

 

Apache Tomcat Examples

 

 
 
 

通过浏览器直接访问https://www.zlex.org/examples/你也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!
Java加密技术(十)——单向认证

博客分类:
 
Java/Security
Javatomcatkeytoolkeystore
    在Java 加密技术(九)中,我们使用自签名证书完成了认证。接下来,我们使用第三方CA签名机构完成证书签名。
    这里我们使用thawte提供的测试用21天免费ca证书。
    1.要在该网站上注明你的域名,这里使用www.zlex.org作为测试用域名(请勿使用该域名作为你的域名地址,该域名受法律保护!请使用其他非注册域名!)。
    2.如果域名有效,你会收到邮件要求你访问https://www.thawte.com/cgi/server/try.exe获得ca证书。
    3.复述密钥库的创建。
   
Shell代码  
keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore  


在这里我使用的密码为 123456

控制台输出:
Console代码  
输入keystore密码:  
再次输入新密码:  
您的名字与姓氏是什么?  
  [Unknown]:  www.zlex.org  
您的组织单位名称是什么?  
  [Unknown]:  zlex  
您的组织名称是什么?  
  [Unknown]:  zlex  
您所在的城市或区域名称是什么?  
  [Unknown]:  BJ  
您所在的州或省份名称是什么?  
  [Unknown]:  BJ  
该单位的两字母国家代码是什么  
  [Unknown]:  CN  
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?  
  [否]:  Y  
 
输入的主密码  
        (如果和 keystore 密码相同,按回车):  
再次输入新密码:  


    4.通过如下命令,从zlex.keystore中导出CA证书申请。
   
Shell代码  
keytool -certreq -alias www.zlex.org -file d:\zlex.csr -keystore d:\zlex.keystore -v  
你会获得zlex.csr文件,可以用记事本打开,内容如下格式:
Text代码  
-----BEGIN NEW CERTIFICATE REQUEST-----  
MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG  
A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG  
SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc  
vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2  
j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD  
gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO  
oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ  
1UbH3+nqMUyCrZgURFslOUY=  
-----END NEW CERTIFICATE REQUEST-----  

    5.将上述文件内容拷贝到https://www.thawte.com/cgi/server/try.exe中,点击next,获得回应内容,这里是p7b格式。
内容如下:
Text代码  
-----BEGIN PKCS7-----  
MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID  
EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL  
MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx  
HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF  
U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw  
MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC  
SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT  
BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA  
keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd  
8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B  
tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV  
HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E  
OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl  
cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v  
Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz  
xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC  
X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj  
q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA  
MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg  
T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF  
U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2  
MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD  
VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg  
Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT  
E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB  
ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF  
cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl  
+AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzARMA8GA1Ud  
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E  
fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew  
A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a  
12rFAQS2BkIk7aU+ghYxAA==  
-----END PKCS7-----  
将其存储为zlex.p7b
    6.将由CA签发的证书导入密钥库。
   
Shell代码  
keytool -import -trustcacerts -alias www.zlex.org -file d:\zlex.p7b -keystore d:\zlex.keystore -v  


在这里我使用的密码为 123456

    控制台输出:
Console代码  
输入keystore密码:  
 
回复中的最高级认证:  
 
所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR  
 TESTING PURPOSES ONLY, C=ZA  
签发人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR  
 TESTING PURPOSES ONLY, C=ZA  
序列号:0  
有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021  
证书指纹:  
         MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4  
         SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA  
         签名算法名称:MD5withRSA  
         版本: 3  
 
扩展:  
 
#1: ObjectId: 2.5.29.19 Criticality=true  
BasicConstraints:[  
  CA:true  
  PathLen:2147483647  
]  
 
 
... 是不可信的。 还是要安装回复? [否]:  Y  
认证回复已安装在 keystore中  
[正在存储 d:\zlex.keystore]  


    7.域名定位
    将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。

    8.配置server.xml
Xml代码  
    keystoreFile="conf/zlex.keystore"  
    keystorePass="123456"   
    truststoreFile="conf/zlex.keystore"      
    truststorePass="123456"       
    SSLEnabled="true"  
    URIEncoding="UTF-8"  
    clientAuth="false"            
    maxThreads="150"  
    port="443"  
    protocol="HTTP/1.1"  
    scheme="https"  
    secure="true"  
    sslProtocol="TLS" />  


将文件zlex.keystore拷贝到tomcat的conf目录下,重新启动tomcat。访问https://www.zlex.org/,我们发现联网有些迟钝。大约5秒钟后,网页正常显示,同时有如下图所示:


 
浏览器验证了该CA机构的有效性。

打开证书,如下图所示:


 

调整测试类:
Java代码  
import static org.junit.Assert.*;  
 
import java.io.DataInputStream;  
import java.io.InputStream;  
import java.net.URL;  
 
import javax.net.ssl.HttpsURLConnection;  
 
import org.junit.Test;  
 
/**
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class CertificateCoderTest {  
    private String password = "123456";  
    private String alias = "www.zlex.org";  
    private String certificatePath = "d:/zlex.cer";  
    private String keyStorePath = "d:/zlex.keystore";  
 
    @Test  
    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "Ceritifcate";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
                certificatePath);  
 
        byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
                keyStorePath, alias, password);  
        String outputStr = new String(decrypt);  
 
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
 
        // 验证数据一致  
        assertArrayEquals(data, decrypt);  
 
        // 验证证书有效  
        assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
 
    }  
 
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
 
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
                keyStorePath, alias, password);  
 
        byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
                certificatePath);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名  
        String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
                password);  
        System.err.println("签名:\r" + sign);  
 
        // 验证签名  
        boolean status = CertificateCoder.verify(encodedData, sign,  
                certificatePath);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
 
    }  
 
    @Test  
    public void testHttps() throws Exception {  
        URL url = new URL("https://www.zlex.org/examples/");  
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();  
 
        conn.setDoInput(true);  
        conn.setDoOutput(true);  
 
        CertificateCoder.configSSLSocketFactory(conn, password, keyStorePath,  
                keyStorePath);  
 
        InputStream is = conn.getInputStream();  
 
        int length = conn.getContentLength();  
 
        DataInputStream dis = new DataInputStream(is);  
        byte[] data = new byte[length];  
        dis.readFully(data);  
 
        dis.close();  
        conn.disconnect();  
        System.err.println(new String(data));  
    }  
}  


再次执行,验证通过!
由此,我们了基于SSL协议的认证过程。测试类的testHttps方法模拟了一次浏览器的HTTPS访问。
Java加密技术(十一)——双向认证

博客分类:
 
Java/Security
opensslpemcaPKCS#7PKCS#10
对于双向认证,做一个简单的描述。
服务器端下发证书,客户端接受证书。证书带有公钥信息,用于验证服务器端、对数据加密/解密,起到OSI五类服务的认证(鉴别)服务和保密性服务。

这只是单向认证,为什么?因为客户端可以验证服务器端,但服务器端不能验证客户端!
如果客户端也有这样一个证书,服务器端也就能够验证客户端,这就是双向认证了!

换言之,当你用银行的“U盾”之类的U盘与银行账户交互时,在你验证银行服务器的同时,服务器也在验证你!这种双重验证,正是网银系统的安全关键!

单向认证见Java加密技术(十)
双向认证需要一个CA机构签发这样的客户端、服务器端证书,首先需要CA机构构建一个根证书。keytool可以构建证书但不能构建我们需要的根证书,openssl则可以!

根证书签发客户端证书,根私钥签发服务器端证书!

我们直接使用linux下的openssl来完成CA,需要修改openssl.cnf文件,在ubuntu下的/etc/ssl/目录下,找到[ CA_default ]修改dir变量。
原文

引用
[ CA_default ]

#dir     = ./demoCA     # Where everything is kept

我们把c盘的ca目录作为CA认证的根目录,文件修改后如下所示:
引用
[ CA_default ]

dir     = $ENV::HOME/ca     # Where everything is kept


我们需要在用户目录下构建一个ca目录,以及子目录,如下所下:
ca
|__certs
|__newcerts
|__private
|__crl

执行如下操作:
Shell代码  
#!/bin/bash     
 
ca_path=ca  
certs_path=$ca_path/certs  
newcerts_path=$ca_path/newcerts  
private_path=$ca_path/private  
crl_path=$ca_path/crl  
 
echo 移除CA根目录     
rm -rf ca     
    
echo 构建CA根目录     
mkdir ca     
   
echo 构建子目录     
mkdir certs     
mkdir newcerts     
mkdir private     
mkdir crl    
 
#构建文件     
touch $ca_path/index.txt  
echo 01 > $ca_path/serial  
echo      
 
#构建随机数     
openssl rand -out $private_path/.rand 1000  
echo      
 
echo 生成根证书私钥     
openssl genrsa -des3 -out $private_path/ca.pem 2048  
echo     
 
echo 查看私钥信息  
openssl rsa -noout -text -in $private_path/ca.pem  
echo  
 
echo 生成根证书请求      
openssl req -new -key $private_path/ca.pem -out $certs_path/ca.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=ca.zlex.org"  
echo     
 
echo 查看证书请求   
openssl req -in $certs_path/ca.csr -text -noout  
echo   
 
echo 签发根证书     
openssl ca -create_serial -out $certs_path/ca.crt -days 3650 -batch -keyfile $private_path/ca.pem -selfsign -extensions v3_ca -infiles $certs_path/ca.csr   
#openssl x509 -req -sha1 -extensions v3_ca -signkey $private_path/ca.pem -in $certs_path/ca.csr -out $certs_path/ca.crt -days 3650  
echo     
 
echo 查看证书详情  
openssl x509 -in $certs_path/ca.crt -text -noout  
echo  
 
echo 证书转换——根证书     
openssl pkcs12 -export -clcerts -in $certs_path/ca.crt -inkey $private_path/ca.pem -out $certs_path/ca.p12  
echo    
 
echo 生成服务器端私钥     
openssl genrsa -des3 -out $private_path/server.pem 1024  
echo     
 
echo 查看私钥信息  
openssl rsa -noout -text -in $private_path/server.pem  
echo  
 
echo 生成服务器端证书请求     
openssl req -new -key $private_path/server.pem -out $certs_path/server.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org"  
echo     
 
echo 查看证书请求   
openssl req -in $certs_path/server.csr -text -noout  
echo   
 
echo 签发服务器端证书  
openssl ca -in $certs_path/server.csr -out $certs_path/server.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext  
#openssl x509 -req -days 365 -sha1 -extensions v3_req -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -CAcreateserial -in $certs_path/server.csr -out $certs_path/server.crt  
echo  
 
echo 查看证书详情  
openssl x509 -in $certs_path/server.crt -text -noout  
echo  
 
echo 证书转换——服务器端     
openssl pkcs12 -export -clcerts -in $certs_path/server.crt -inkey $private_path/server.pem -out $certs_path/server.p12  
echo      
 
echo 生成客户端私钥     
openssl genrsa -des3 -out $private_path/client.pem 1024  
echo     
 
echo 生成客户端私钥     
openssl genrsa -des3 -out $private_path/client.pem 1024  
echo     
 
echo 查看私钥信息  
openssl rsa -noout -text -in $private_path/client.pem  
echo  
 
echo 生成客户端证书请求     
openssl req -new -key $private_path/client.pem -out $certs_path/client.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=zlex"  
echo     
 
echo 查看证书请求   
openssl req -in $certs_path/client.csr -text -noout  
echo   
 
echo 签发客户端证书     
openssl ca -in $certs_path/client.csr -out $certs_path/client.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext  
#openssl x509 -req -days 365 -sha1 -extensions dir_sect -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -in $certs_path/client.csr -out $certs_path/client.crt  
echo     
 
echo 查看证书详情  
openssl x509 -in $certs_path/client.crt -text -noout  
echo  
 
echo 证书转换——客户端     
openssl pkcs12 -export -clcerts -in $certs_path/client.crt -inkey $private_path/client.pem -out $certs_path/client.p12  
echo   
 
echo 生成证书链PKCS#7  
openssl crl2pkcs7 -nocrl -certfile $certs_path/server.crt -certfile $certs_path/ca.crt -certfile $certs_path/client.crt -out  
form PEM -out $certs_path/zlex.p7b  
echo  
 
echo 查看证书链  
openssl pkcs7 -in $certs_path/zlex.p7b -print_certs -noout  


这个脚本就是最重要的结晶了!

执行结果,如下:
引用

生成根证书私钥
Generating RSA private key, 2048 bit long modulus
..................................+++
.............................................................+++
e is 65537 (0x10001)
Enter pass phrase for ca/private/ca.pem:
Verifying - Enter pass phrase for ca/private/ca.pem:

查看私钥信息
Enter pass phrase for ca/private/ca.pem:
Private-Key: (2048 bit)
modulus:
    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a:
    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20:
    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6:
    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83:
    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c:
    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61:
    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06:
    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12:
    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f:
    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d:
    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33:
    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec:
    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42:
    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd:
    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb:
    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32:
    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48:
    f3:d1
publicExponent: 65537 (0x10001)
privateExponent:
    00:b0:8a:e4:43:1c:df:6e:bc:6f:e0:80:76:c4:8a:
    75:5a:0b:d1:4d:61:cb:b5:1b:6b:24:c7:47:69:ad:
    b5:ee:d2:73:a1:21:4e:95:ca:69:9a:a8:3f:40:c2:
    7e:dc:c3:c0:bc:d2:0f:5a:ba:9b:7c:76:dc:46:e0:
    42:14:27:34:a1:af:67:68:ad:dc:d8:24:94:91:c1:
    ee:db:ba:78:be:87:e3:7f:31:4b:4e:c6:f2:e2:48:
    69:d4:c1:82:94:33:8b:84:15:ff:3e:72:c0:ed:20:
    40:28:5e:c9:8f:39:b8:5b:df:81:89:8f:13:cc:68:
    93:6d:64:58:20:3c:0a:82:ce:ec:2f:9b:b2:9d:ca:
    e7:19:22:98:29:6e:7c:4d:85:45:17:50:8f:5d:b1:
    45:be:42:af:1a:7f:84:26:b4:5d:a6:22:8a:07:e8:
    b3:b4:5a:59:45:20:b5:ef:1c:81:25:9e:73:74:04:
    d6:57:30:2c:a7:25:50:7c:d7:87:73:b3:d0:c2:8b:
    c9:02:8e:15:9e:40:41:a5:7a:a9:d8:85:fb:5b:9a:
    59:83:bc:80:fa:74:e6:88:14:70:33:61:d7:f5:51:
    47:8f:60:51:cb:c4:97:66:65:94:f0:ed:58:ca:80:
    c1:89:e0:55:68:4c:69:21:0f:08:27:e0:87:11:df:
    b7:bd
prime1:
    00:f7:ff:b0:40:de:62:b6:a2:e5:d0:f5:fa:28:3d:
    d3:30:30:89:8f:d1:ae:df:e9:09:ee:a0:b0:a5:a5:
    a4:e5:93:97:7e:e6:0b:09:70:4c:62:99:5e:7d:45:
    2f:fd:21:5a:31:d9:26:7f:39:5f:6e:eb:36:02:4e:
    18:99:1b:38:13:99:f5:f3:a3:6b:93:83:67:fb:58:
    67:d4:07:eb:e3:2f:31:b3:97:8f:f6:86:1f:15:08:
    1a:4b:b5:a8:06:97:72:9c:74:ab:53:1f:ac:ee:fb:
    59:03:39:a6:5c:a8:77:43:c0:2c:14:60:0e:71:3d:
    70:b6:59:09:40:86:04:54:bf
prime2:
    00:da:f0:73:2c:bd:52:a5:0d:9a:40:c4:34:fc:c9:
    cf:0f:67:8a:02:01:ca:e7:b8:4e:57:da:0c:0d:b2:
    f9:f3:f2:e4:4c:82:61:aa:04:2c:88:39:18:bd:86:
    d6:dc:d0:e9:6c:c6:6f:d9:87:59:57:9b:1a:6b:c9:
    56:c1:4d:33:ce:3e:15:b9:42:4e:e0:f8:14:91:c3:
    fe:63:b2:13:29:99:a7:a6:13:cc:f8:9c:38:29:28:
    dd:ed:d1:a3:7c:05:2c:26:a0:84:c6:09:9e:42:ef:
    7b:5e:50:c7:57:e3:bc:02:93:0b:74:a1:b5:0b:6e:
    23:18:8b:82:6f:ac:3c:0b:6f
exponent1:
    7c:a1:23:4b:46:37:27:7f:6f:ac:f6:a0:93:ae:96:
    3e:46:76:2b:2f:7e:09:8a:8c:72:3e:90:e7:7d:fa:
    03:61:8b:a5:bb:27:da:c3:73:af:ad:51:9d:f4:b2:
    2c:2c:a1:ae:21:69:c6:4f:e7:d4:cf:21:a2:40:ea:
    fd:ae:7f:1c:e2:a7:86:9c:1e:c8:d0:25:e6:5b:44:
    3a:7b:0c:a1:6c:2b:37:0c:b8:cd:74:13:94:b7:30:
    b7:d1:7f:b2:68:53:b1:aa:b4:1a:9e:f5:82:58:10:
    20:9d:cd:2c:0d:81:7a:2b:ce:3b:23:16:be:f3:d8:
    7b:da:fc:da:4f:3f:47:f3
exponent2:
    66:c9:5c:49:34:d9:08:04:4a:d6:fd:46:a3:27:5b:
    be:af:ad:6b:23:cc:4e:dd:88:6a:56:44:32:6a:44:
    4e:f3:49:9b:61:da:d8:26:fd:81:36:cd:16:ad:a7:
    52:24:02:72:be:f6:e3:f9:57:48:79:d8:fd:a1:98:
    c9:47:a5:7a:be:4b:14:9e:bc:c9:81:ae:a6:80:8d:
    7d:e0:ac:7e:6b:54:f9:f3:71:d7:86:00:17:d2:c7:
    de:4e:fd:a1:cc:0b:de:56:9d:ff:1b:a4:e1:67:ed:
    53:6a:39:2c:5a:0e:7a:66:ee:89:e3:21:4c:2c:78:
    ed:9d:11:af:bb:fc:b4:a1
coefficient:
    00:b1:23:a8:cc:b1:5e:2e:38:09:0c:b5:df:2c:c6:
    15:e8:08:48:45:b9:9d:ec:6f:27:45:5b:a7:bc:b6:
    b1:ec:a5:39:b4:40:8e:bc:40:1f:b9:4d:14:2e:18:
    fb:87:1e:20:91:34:58:e3:ac:c3:4a:dc:a8:2a:97:
    ce:aa:8d:62:0e:91:af:1f:53:d6:37:55:1d:14:9c:
    01:98:34:77:28:d7:cf:f7:a0:2d:73:40:48:5e:ed:
    ae:9b:15:42:06:e6:a3:5a:2b:b0:bc:ee:7a:bb:52:
    e6:28:19:c2:e5:de:6f:4d:fa:fb:69:81:7b:13:2b:
    01:87:bf:bf:66:8f:24:a1:8f

生成根证书请求
Enter pass phrase for ca/private/ca.pem:

查看证书请求
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=ca.zlex.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a:
                    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20:
                    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6:
                    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83:
                    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c:
                    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61:
                    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06:
                    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12:
                    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f:
                    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d:
                    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33:
                    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec:
                    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42:
                    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd:
                    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb:
                    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32:
                    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48:
                    f3:d1
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha1WithRSAEncryption
        af:91:f8:56:6f:db:de:cb:df:2c:87:93:99:ac:4b:51:12:a2:
        c1:2b:09:d2:58:7c:e1:07:5c:53:9f:f3:e1:b6:3a:e9:08:e7:
        65:89:3b:0a:01:83:24:a3:b5:74:65:50:a5:77:bc:30:1b:7d:
        80:8b:4c:92:ec:81:91:6e:b7:8f:05:e7:1d:b2:89:84:18:8c:
        5f:66:be:19:15:ba:ba:c3:f7:0d:c3:7d:7a:11:47:17:e5:cf:
        87:69:2e:15:91:d7:db:9d:8e:c9:0f:81:71:fa:00:93:33:2c:
        99:e1:be:76:06:f1:8a:e6:8b:1d:9b:07:70:f0:f2:44:91:ed:
        a2:ed:28:91:5f:6a:8a:f3:cf:ab:0d:b3:05:30:72:19:86:ae:
        c6:2d:a4:22:9f:21:cf:55:0c:b7:79:44:01:6e:36:43:a5:dc:
        a0:ea:46:2a:b0:9d:b3:53:4a:57:fc:72:1b:4c:52:cc:a3:39:
        d6:49:d6:f4:8c:e2:bf:5a:a6:6e:69:7c:f2:bc:7b:02:b7:f5:
        91:7f:94:2b:8c:58:0f:aa:a3:72:93:46:fe:08:29:08:51:eb:
        c6:a0:4e:7a:e1:bd:c6:0b:11:9d:63:96:af:22:ee:7b:79:84:
        cd:e7:f0:23:17:e7:9f:a2:73:c5:15:e1:f5:a1:af:8d:58:f5:
        e0:eb:57:fd

签发根证书
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for ca/private/ca.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Jul 24 08:15:59 2012 GMT
            Not After : Jul 22 08:15:59 2022 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = BJ
            organizationName          = zlex
            organizationalUnitName    = zlex
            commonName                = ca.zlex.org
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9
                DirName:/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org
                serial:01

            X509v3 Basic Constraints:
                CA:TRUE
Certificate is to be certified until Jul 22 08:15:59 2022 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated

查看证书详情
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org
        Validity
            Not Before: Jul 24 08:15:59 2012 GMT
            Not After : Jul 22 08:15:59 2022 GMT
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:d4:18:ab:5f:ad:b7:d0:09:d4:68:63:b5:db:8a:
                    d1:a1:db:7e:f3:bb:bb:c2:be:a7:35:17:9e:bb:20:
                    d3:1f:ed:63:e7:7d:29:6d:d2:7c:60:06:47:53:a6:
                    23:b0:bd:94:65:3f:57:1e:00:51:f3:a1:9a:1b:83:
                    14:a5:53:72:86:21:a2:57:22:2f:6a:a9:46:50:8c:
                    f0:51:cf:e6:83:5b:23:dc:f9:ea:6c:2e:51:20:61:
                    d1:84:9f:28:e8:01:89:b5:cb:55:68:4a:11:b1:06:
                    56:31:21:16:c8:ac:2b:68:31:e1:de:12:d3:21:12:
                    83:36:4c:ca:a8:b5:7e:b9:a7:63:4e:8e:e0:79:0f:
                    0e:91:36:28:7c:dd:9a:e2:e0:98:8b:91:7f:09:7d:
                    20:bb:37:f2:ab:aa:f0:ef:ae:68:7e:db:ca:db:33:
                    84:48:5a:e3:ff:0b:08:0e:96:6d:01:c8:12:35:ec:
                    9f:31:55:7f:53:7e:bd:fb:c4:16:b8:1f:17:29:42:
                    0f:0e:04:57:14:18:fd:e5:d6:3f:40:04:cd:85:dd:
                    d3:eb:2f:9a:bf:3c:8a:60:01:88:2f:43:0a:8b:bb:
                    50:13:f8:cc:68:f9:10:eb:f9:7e:63:de:62:55:32:
                    a8:fe:ce:51:67:79:c9:a6:3b:a3:c9:d7:81:7c:48:
                    f3:d1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9
                DirName:/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org
                serial:01

            X509v3 Basic Constraints:
                CA:TRUE
    Signature Algorithm: sha1WithRSAEncryption
        8a:99:b8:17:fc:64:7b:88:9c:1b:91:23:60:f4:5c:51:16:9a:
        9f:42:b4:d3:a5:bb:79:ca:78:e3:fc:a7:af:66:da:ec:5a:8c:
        81:c1:aa:04:32:a9:59:e0:d6:6a:f2:37:38:97:70:a5:27:5d:
        14:73:2e:2d:73:78:1d:37:2c:04:f7:c3:99:9d:be:0c:dd:2a:
        27:2c:0f:6e:95:96:01:c7:4c:99:f7:49:69:f9:ba:cb:62:b8:
        c6:43:6c:5b:b5:cd:25:42:a7:fb:81:27:bc:d8:e4:95:26:7d:
        da:50:f8:b8:be:0a:3d:54:35:d0:9d:22:e7:f0:f0:4c:7d:b4:
        57:2e:98:91:1a:1d:49:e5:8e:48:f6:2b:54:7e:04:fc:1c:e3:
        52:f7:04:f6:9b:bb:84:25:31:f7:31:6e:7f:fa:4c:e4:15:a2:
        86:0a:1a:56:8c:ad:07:49:fb:bc:28:27:a3:95:ba:eb:b3:28:
        db:11:78:ef:84:fc:3c:16:df:58:39:2e:14:8d:89:fe:7a:d2:
        24:eb:a7:66:11:8c:88:55:40:e1:c3:3b:95:b2:bc:af:36:0e:
        92:a8:cd:62:d5:57:9c:11:1b:f6:a1:36:5f:25:6c:16:c5:e2:
        68:19:e7:12:3d:4b:07:24:81:e6:71:f9:59:c5:f9:1c:62:6d:
        b3:24:b9:8a

证书转换——根证书
Enter pass phrase for ca/private/ca.pem:
Enter Export Password:
Verifying - Enter Export Password:

生成服务器端私钥
Generating RSA private key, 1024 bit long modulus
......................................................++++++
................++++++
e is 65537 (0x10001)
Enter pass phrase for ca/private/server.pem:
Verifying - Enter pass phrase for ca/private/server.pem:

查看私钥信息
Enter pass phrase for ca/private/server.pem:
Private-Key: (1024 bit)
modulus:
    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31:
    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00:
    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5:
    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3:
    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f:
    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34:
    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f:
    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5:
    00:bd:d9:e9:4e:2d:87:d5:95
publicExponent: 65537 (0x10001)
privateExponent:
    4d:da:15:fd:6c:24:37:c1:bf:30:f8:be:af:09:a3:
    55:20:b1:ff:f3:70:37:d5:1d:16:99:c1:2c:c9:9b:
    6c:69:e4:ae:d7:93:d8:7a:54:6a:cd:5a:b5:7e:0c:
    0c:71:ac:41:76:0a:67:05:23:11:c9:94:81:0f:a6:
    0d:07:ee:a4:26:0e:20:ff:36:6c:f7:2d:fa:8e:39:
    85:f8:b8:1a:e0:be:26:f8:24:3c:d4:d0:a0:89:9c:
    48:15:d9:28:de:51:dd:14:3f:ca:c9:63:ed:5d:e4:
    50:b0:06:5e:1b:f8:99:b4:49:f6:d6:cb:60:8a:7b:
    fa:f8:6e:86:44:55:e5:45
prime1:
    00:ef:cc:38:ab:e6:98:71:09:32:5c:69:b3:e0:59:
    9d:d7:7a:f9:e3:b9:cd:a8:84:74:1a:91:2a:db:2c:
    96:40:5a:28:0b:99:6c:da:fa:ca:83:54:e0:59:06:
    84:df:55:9a:04:9c:1c:6b:54:52:d5:31:d7:f9:0e:
    9a:13:b0:ed:03
prime2:
    00:e7:a2:c3:03:55:d7:54:7c:3a:38:40:f1:ac:9a:
    e8:dd:3a:5c:24:a6:78:34:c4:ce:24:c8:31:de:5a:
    0e:df:09:df:7c:ad:36:14:e0:be:6d:2c:58:89:c6:
    7e:ec:51:82:68:81:91:ed:b5:04:ff:c0:61:8e:aa:
    5b:ee:6b:f3:87
exponent1:
    2a:22:0c:d7:0f:56:3b:8e:2d:1e:15:a8:78:43:e6:
    ba:e4:ad:a1:78:95:0d:05:f0:cc:76:33:3c:7d:52:
    0d:0e:8a:38:b7:85:6b:d8:62:da:be:80:08:c4:5f:
    76:4a:39:1c:94:3d:5e:12:5b:d7:7f:c1:7d:ce:35:
    fe:3d:b8:f7
exponent2:
    00:94:0b:ec:36:52:84:19:04:79:35:81:14:b5:ec:
    20:8f:5d:00:8d:90:34:5e:0d:b7:6f:bc:e0:5a:ac:
    16:bb:29:15:45:1b:73:e8:6e:28:67:a0:a3:4a:13:
    ab:05:a1:a7:06:e2:61:81:9b:64:01:8e:55:0c:19:
    08:3e:df:92:3b
coefficient:
    00:8e:4e:ee:04:55:cc:4f:0f:c0:02:a4:9d:08:a8:
    4b:ec:72:7c:86:27:a9:0a:5e:1c:94:65:9e:c6:8a:
    6a:5c:9b:76:5d:c0:ae:f8:36:61:15:3b:67:fb:15:
    b3:cf:f4:2c:9b:56:66:13:89:89:69:01:d9:6e:b0:
    f7:02:d4:06:c9

生成服务器端证书请求
Enter pass phrase for ca/private/server.pem:

查看证书请求
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=www.zlex.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31:
                    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00:
                    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5:
                    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3:
                    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f:
                    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34:
                    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f:
                    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5:
                    00:bd:d9:e9:4e:2d:87:d5:95
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha1WithRSAEncryption
        2b:e9:b9:0b:e0:94:56:95:dd:59:1e:19:16:e0:f9:73:db:50:
        63:d3:d4:4d:5c:9b:98:9f:a7:6d:9b:4d:ae:67:52:18:e1:42:
        b0:66:7c:75:6a:db:98:bc:e6:47:08:aa:55:ca:ce:35:5c:5a:
        60:8b:7b:c8:f0:10:8a:bd:5f:d7:c8:b8:48:03:18:7e:68:6e:
        69:35:9c:c8:b0:c8:65:43:43:25:35:d7:d2:70:45:55:ab:78:
        51:4d:22:c3:68:b2:97:b5:3c:86:e8:2b:43:de:5d:e4:b0:b5:
        0e:eb:84:9d:42:81:ee:e0:0a:48:40:6a:93:a4:bd:3a:45:6f:
        20:24

签发服务器端证书
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for ca/private/ca.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jul 24 08:16:15 2012 GMT
            Not After : Jul 24 08:16:15 2013 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = BJ
            organizationName          = zlex
            organizationalUnitName    = zlex
            commonName                = www.zlex.org
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                CF:79:10:96:42:84:0C:51:DE:6E:DB:3C:5B:08:F1:E1:EB:0C:26:B9
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9

Certificate is to be certified until Jul 24 08:16:15 2013 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

查看证书详情
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 2 (0x2)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org
        Validity
            Not Before: Jul 24 08:16:15 2012 GMT
            Not After : Jul 24 08:16:15 2013 GMT
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=www.zlex.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:d8:f9:bd:0a:a8:d3:97:98:b2:22:af:29:a9:31:
                    76:50:52:77:c8:3b:7c:91:75:db:b3:63:88:cc:00:
                    be:1a:6c:e6:80:23:90:37:5f:1a:d3:80:f2:7f:b5:
                    77:01:ec:85:3e:4e:c0:af:0d:77:c0:a5:8b:bc:c3:
                    fe:70:91:66:17:a4:ec:23:08:5b:e3:df:a3:40:2f:
                    e6:83:bd:3f:d0:62:9c:c0:36:ad:e7:cb:13:e8:34:
                    d7:6a:66:57:f5:bb:94:2f:7c:d5:27:7b:ee:e6:4f:
                    fc:ff:c1:a4:01:96:d6:a0:b8:46:1d:93:02:a6:c5:
                    00:bd:d9:e9:4e:2d:87:d5:95
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                CF:79:10:96:42:84:0C:51:DE:6E:DB:3C:5B:08:F1:E1:EB:0C:26:B9
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9

    Signature Algorithm: sha1WithRSAEncryption
        3d:85:0a:f5:a6:8e:f5:13:1b:fc:74:b6:50:8f:fe:0c:e6:32:
        0e:0c:5a:0a:75:2d:e8:15:39:2f:93:46:29:c6:cc:27:5a:36:
        a0:93:f8:bc:38:d5:2d:55:b9:19:de:81:6f:b6:5f:1f:07:65:
        81:c5:12:4e:ea:3e:09:d0:d5:b8:66:c1:cd:4d:5d:51:19:a1:
        7f:7b:cb:dc:bf:b0:be:3e:f8:8b:74:d3:31:a9:95:a3:ef:25:
        a3:1e:98:65:0f:d4:40:51:ef:42:02:72:f0:59:26:8a:e7:d6:
        ca:34:ad:fb:3d:a8:e7:05:93:a6:78:bd:b5:90:51:83:06:2b:
        95:db:01:0c:89:9f:74:a4:32:89:c5:15:c6:ec:e2:61:10:29:
        70:da:c5:ea:d6:9c:be:c3:4c:a1:42:6a:26:2f:23:7c:90:51:
        8f:51:ee:49:c9:6b:9c:0c:15:a2:d3:dc:90:19:db:4d:d1:ad:
        ca:06:d1:e1:60:20:18:b1:6d:0b:17:f7:06:e6:e8:d1:b0:0c:
        6d:55:16:f1:63:54:da:c2:3f:6c:e5:99:68:7a:a0:fa:29:5c:
        dc:cf:34:90:fb:91:7b:e0:5d:bb:a0:9d:91:f3:17:bd:0b:5a:
        69:d7:0c:24:75:ca:b2:08:da:bf:67:35:ce:01:d0:4e:45:81:
        97:bd:fb:87

证书转换——服务器端
Enter pass phrase for ca/private/server.pem:
Enter Export Password:
Verifying - Enter Export Password:

生成客户端私钥
Generating RSA private key, 1024 bit long modulus
..++++++
...........++++++
e is 65537 (0x10001)
Enter pass phrase for ca/private/client.pem:
Verifying - Enter pass phrase for ca/private/client.pem:

查看私钥信息
Enter pass phrase for ca/private/client.pem:
Private-Key: (1024 bit)
modulus:
    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e:
    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75:
    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1:
    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec:
    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e:
    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7:
    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc:
    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a:
    1b:65:f0:4d:1c:0b:1f:50:eb
publicExponent: 65537 (0x10001)
privateExponent:
    3a:35:b2:8d:73:af:fd:55:62:e5:f2:9e:dc:42:d5:
    f8:a3:15:a0:c7:0e:3f:d6:e0:d6:a7:df:77:20:86:
    bb:43:4c:14:cc:c5:3b:8f:3f:0d:14:ca:7e:a6:72:
    02:c1:16:c7:83:d3:ad:05:96:49:18:38:ae:d7:92:
    b3:eb:2e:05:43:d6:3d:04:3c:0b:fc:15:79:c5:85:
    10:ed:21:6e:30:73:0b:a6:4f:9a:fe:db:4a:98:bc:
    ec:03:7b:7f:e6:16:2f:a5:f3:5e:0d:cf:ce:eb:4a:
    3e:c5:b9:7f:fc:4c:60:9e:0e:d4:aa:91:5a:46:f7:
    b3:77:fc:0b:1b:62:70:b9
prime1:
    00:ef:6d:7f:92:6a:af:21:59:ed:fe:49:a8:7c:4a:
    1d:4d:7c:f9:38:bf:e7:dc:42:41:e1:33:f9:e1:c7:
    74:45:2e:1c:e4:40:8d:5f:1a:ac:11:9e:a4:6c:1d:
    00:6d:4e:aa:4d:58:e9:92:84:ac:d9:29:67:e0:79:
    a8:a3:15:e3:2d
prime2:
    00:c1:6f:21:c5:62:48:78:3a:0f:25:98:00:46:d6:
    c2:2d:0f:96:fb:20:4b:f4:03:81:71:3f:6f:30:c0:
    f3:a6:e6:f4:00:a4:fa:0b:97:e6:2a:21:8c:cb:c1:
    28:eb:5f:f6:01:62:85:9a:37:98:e7:53:a4:8b:3f:
    bd:77:eb:f3:77
exponent1:
    00:e3:71:e0:9b:85:af:22:7e:9c:a0:50:f6:b6:43:
    6d:bc:bb:b8:c0:d9:44:f8:2f:15:08:4b:68:d8:bb:
    b1:cf:3a:34:05:fc:f0:8f:64:f6:0a:b2:ea:bd:2d:
    7b:c7:5a:d0:5b:33:d8:86:f0:74:86:c3:57:c3:9d:
    ae:be:66:3f:6d
exponent2:
    00:82:4a:c9:04:9b:5f:15:1c:86:77:5c:1b:53:9b:
    f4:cf:45:60:fd:66:93:c2:99:59:e7:5e:43:17:23:
    e0:fa:db:36:1f:f9:00:34:2e:ec:ea:14:0f:32:6f:
    b9:90:51:e2:f2:ab:da:32:36:a0:d7:b0:8f:74:fc:
    4a:33:2c:cb:a1
coefficient:
    51:c1:7e:d7:0d:98:86:cb:ca:41:ea:aa:54:6c:00:
    49:c3:18:12:c4:5b:75:fe:0d:0c:e2:2f:0f:93:8e:
    8e:01:c5:9d:ff:40:2b:20:08:24:7f:a5:f2:da:67:
    96:5e:e6:7e:1e:52:32:2f:88:ef:df:20:6a:75:ec:
    28:cd:fa:a0

生成客户端证书请求
Enter pass phrase for ca/private/client.pem:

查看证书请求
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=CN, ST=BJ, L=BJ, O=zlex, OU=zlex, CN=zlex
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e:
                    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75:
                    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1:
                    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec:
                    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e:
                    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7:
                    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc:
                    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a:
                    1b:65:f0:4d:1c:0b:1f:50:eb
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha1WithRSAEncryption
        91:5b:b2:2e:b3:54:14:92:7a:44:c0:59:11:0f:fe:08:50:33:
        09:0f:73:d3:9d:15:43:07:66:4a:9e:7c:de:12:4d:bc:b6:3a:
        7a:6b:36:40:3a:4b:ea:db:f7:2e:a1:de:ce:4f:a6:98:14:3b:
        c0:f6:3d:fe:db:82:fa:c7:f1:1e:9a:6c:2b:ff:e6:a4:91:b1:
        ab:20:44:91:a8:d9:1b:13:8f:9e:24:68:16:f3:c1:66:7b:3b:
        29:b5:61:3d:be:88:00:d8:0a:1c:63:f0:25:6c:33:7d:86:80:
        54:d5:75:db:6f:7e:9c:52:4c:70:0d:5a:88:ae:b5:1a:12:41:
        e4:47

签发客户端证书
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for ca/private/ca.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 3 (0x3)
        Validity
            Not Before: Jul 24 08:16:35 2012 GMT
            Not After : Jul 24 08:16:35 2013 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = BJ
            organizationName          = zlex
            organizationalUnitName    = zlex
            commonName                = zlex
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                FD:85:1C:BA:E0:C4:81:F5:F4:92:F1:FC:8A:59:77:33:60:6F:47:F7
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9

Certificate is to be certified until Jul 24 08:16:35 2013 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

查看证书详情
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 3 (0x3)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=BJ, O=zlex, OU=zlex, CN=ca.zlex.org
        Validity
            Not Before: Jul 24 08:16:35 2012 GMT
            Not After : Jul 24 08:16:35 2013 GMT
        Subject: C=CN, ST=BJ, O=zlex, OU=zlex, CN=zlex
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:b4:e9:7d:3d:6b:8b:07:94:7d:47:51:56:3e:0e:
                    92:2f:87:8c:60:0f:b8:cb:eb:90:6d:13:76:51:75:
                    e4:3e:b7:6e:1f:f0:63:5b:f7:ba:51:c0:04:1e:f1:
                    d0:ef:58:4a:35:47:4a:1a:11:72:fc:e9:10:82:ec:
                    3e:0d:ef:7d:17:a0:5e:93:b4:01:8f:a5:27:3c:3e:
                    a9:26:f0:00:ba:ca:24:98:92:51:3e:4b:d0:81:a7:
                    fc:14:e2:98:f5:27:f2:51:4c:a8:ae:b4:5f:e7:cc:
                    70:7e:23:57:92:6a:cf:d4:1d:6f:b3:52:8a:4a:1a:
                    1b:65:f0:4d:1c:0b:1f:50:eb
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                FD:85:1C:BA:E0:C4:81:F5:F4:92:F1:FC:8A:59:77:33:60:6F:47:F7
            X509v3 Authority Key Identifier:
                keyid:7E:C9:9A:37:37:66:AC:79:41:63:F0:61:48:CD:24:39:2F:C2:0E:E9

    Signature Algorithm: sha1WithRSAEncryption
        b2:31:c0:15:a1:8f:2c:6d:61:0c:4f:6e:c1:fe:7a:88:e0:60:
        ce:6d:43:b4:29:d8:4d:83:4d:ea:ce:f0:8e:c1:c7:3b:bd:30:
        cb:92:71:11:7d:19:04:11:58:25:5d:1b:ed:6f:22:13:91:ea:
        13:7f:0e:99:00:ec:fb:b3:a5:e2:b9:ea:ea:bb:35:09:3b:ca:
        f5:49:ac:a1:d3:d5:ae:ff:ce:11:a9:2f:53:74:88:24:9f:f8:
        b2:bc:02:4d:1a:bb:c1:53:3e:6e:31:52:4d:ac:f8:14:bd:b1:
        0d:31:1d:aa:94:43:38:5e:fb:c2:26:3e:43:ba:25:3b:23:27:
        a8:7d:5d:3d:f9:97:28:71:51:1d:a4:56:44:b4:f6:51:4a:2b:
        8b:47:d3:10:49:04:cd:c3:58:62:75:bc:c7:6a:4c:d5:9a:a8:
        e9:9c:23:ec:f8:26:e5:de:43:4e:f2:8d:c2:75:40:70:3f:03:
        0f:74:78:7a:bc:ca:6f:90:a0:3e:3a:d2:92:16:d5:ca:af:93:
        28:1f:24:3a:7e:2c:b9:db:87:10:68:e0:c9:6c:0b:5d:9f:15:
        be:bc:13:22:af:7b:8f:e9:14:51:04:65:7a:69:18:c2:ca:4f:
        cb:e5:4c:62:41:88:b1:ee:ac:43:14:34:6d:58:af:52:b1:25:
        76:f3:0e:8f

证书转换——客户端
Enter pass phrase for ca/private/client.pem:
Enter Export Password:
Verifying - Enter Export Password:

生成证书链

查看证书链
subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=www.zlex.org
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org

subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org

subject=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=zlex
issuer=/C=CN/ST=BJ/O=zlex/OU=zlex/CN=ca.zlex.org



来看一下这3套证书,如下两幅图所示:

CA证书


 


 


 

服务器证书


 


 


 

客户证书


 


 


 

证书链


 

"ca.zlex.org"证书充当了CA根证书,"www.zlex.org"充当服务器端证书,"zlex"充当客户端证书

使用keytool将其导入本地密钥库
导入CA证书
Shell代码  
keytool -import -v -trustcacerts -alias ca.zlex.org -file ca.crt -storepass 123456 -keystore ca.keystore          

控制台输出
引用

所有者:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
签发人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:989b27ef00e53a99
有效期: Wed Jul 18 17:53:51 CST 2012 至Sat Jul 16 17:53:51 CST 2022
证书指纹:
         MD5:BA:14:1F:89:3A:1E:63:7B:20:AC:5A:50:FE:65:7E:16
         SHA1:E0:A4:0E:6F:09:7E:01:27:C0:FC:62:26:1A:0C:C6:7B:BF:6A:18:B3
         签名算法名称:SHA1withRSA
         版本: 1
信任这个认证? [否]:  y
认证已添加至keystore中
[正在存储 ca.keystore]


导入服务器端证书
Shell代码  
keytool -import -v -trustcacerts -alias www.zlex.org -file server.crt -storepass 123456 -keystore server.keystore  

控制台输出
引用

所有者:CN=www.zlex.org, OU=zlex, O=zlex, ST=BJ, C=CN
签发人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:1
有效期: Wed Jul 18 17:54:25 CST 2012 至Thu Jul 18 17:54:25 CST 2013
证书指纹:
         MD5:7E:5E:66:56:AF:E7:F5:72:0F:FC:95:85:97:07:4E:2A
         SHA1:B1:E7:E8:AC:AB:C9:72:69:D8:E2:25:D5:16:A9:AF:C1:B7:4A:74:5D
         签名算法名称:SHA1withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A8 49 2F E2 2D 15 9F 42   BD 76 2B 20 D3 EB A5 EE  .I/.-..B.v+ ....
0010: 31 CA E7 63                                        1..c
]
]

#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

#3: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
[CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN]
SerialNumber: [    989b27ef 00e53a99]
]

#4: ObjectId: 2.16.840.1.113730.1.13 Criticality=false

信任这个认证? [否]:  y
认证已添加至keystore中
[正在存储 server.keystore]


导入客户端证书
Shell代码  
keytool -import -v -trustcacerts -alias client -file client.crt -storepass 123456 -keystore client.keystore  

以下是输出内容:
引用

所有者:CN=zlex, OU=zlex, O=zlex, ST=BJ, C=CN
签发人:CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:2
有效期: Wed Jul 18 17:54:49 CST 2012 至Thu Jul 18 17:54:49 CST 2013
证书指纹:
         MD5:81:16:ED:92:9E:17:DB:E3:BE:DE:CD:8D:F8:E0:EE:C4
         SHA1:C0:E0:42:81:79:70:4C:F8:44:D4:76:2D:C5:62:7C:67:B2:41:B3:AC
         签名算法名称:SHA1withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0C 2C 25 86 C6 8D 04 88   F5 63 19 DC 09 B1 3C 5D  .,%......c....<]
0010: 59 C9 72 1B                                        Y.r.
]
]

#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

#3: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
[CN=ca.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN]
SerialNumber: [    989b27ef 00e53a99]
]

#4: ObjectId: 2.16.840.1.113730.1.13 Criticality=false

信任这个认证? [否]:  y
认证已添加至keystore中
[正在存储 client.keystore]


PS 吊销证书:
Shell代码  
echo 吊销客户端证书  
openssl ca -revoke $certs_path/client.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem   


引用
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for private/ca.pem:
Revoking Certificate 02.
Data Base Updated



生成证书吊销列表文件(CRL)
执行命令如下:
Shell代码  
openssl ca -gencrl -out ca.crl -config "$HOME/testca/conf/testca.conf"  

-crldays和-crlhours参数,说明下一个吊销列表将在多少天后(或多少小时候)发布。

可以用以下命令检查testca.crl的内容:
Shell代码  
openssl crl -in testca.crl -text -noout  


引用
http://blog.csdn.net/gothicane/articles/2865818.aspx
http://www.5dlinux.com/article/7/2009/linux_35291.html
http://www.tc.umn.edu/~brams006/selfsign_ubuntu.html
http://www.tc.umn.edu/~brams006/selfsign.html
http://zhouzhk.iteye.com/blog/136943
http://bbs.cfan.com.cn/thread-743287-1-1.html
http://www.iteye.com/problems/4072
http://blog.csdn.net/jasonhwang/archive/2008/04/26/2329589.aspx
http://blog.csdn.net/jasonhwang/archive/2008/04/29/2344768.aspx
 

 

Java加密技术(十二)——*.PFX(*.p12)&个人信息交换文件

博客分类:
 
Java/Security
pfxkeystorep12keytool
今天来点实际工作中的硬通货!
与计费系统打交道,少不了用到加密/解密实现。为了安全起见,通过非对称加密交换对称加密密钥更是不可或缺。那么需要通过什么载体传递非对称算法公钥/私钥信息?数字证书是公钥的载体,而密钥库可以包含公钥、私钥信息。
JKS和PKCS#12都是比较常用的两种密钥库格式/标准。对于前者,搞Java开发,尤其是接触过HTTPS平台的朋友,并不陌生。JKS文件(通常为*.jks或*.keystore,扩展名无关)可以通过Java原生工具——KeyTool生成;而后者PKCS#12文件(通常为*.p12或*.pfx,意味个人信息交换文件),则是通过更为常用的OpenSSL工具产生。
当然,这两者之间是可以通过导入/导出的方式进行转换的!当然,这种转换需要通过KeyTool工具进行!
回归正题,计费同事遇到一个难题:合作方交给他们一个*.pfx文件,需要他们从中提取密钥,然后进行加密交互。其实,通过Java直接操作密钥库文件(或个人信息交换文件)对于一般Java开发人员来说,这都是个冷门。不接触数字安全,根本不知所云。况且,Java原生的密钥库文件格式为JKS,如何操作*.pfx文件?密钥库操作需要获知密钥库别名,*.pfx别名是什么?!接下来就解决这些问题!

方案:
通过keytool密钥库导入命令importkeystore,将密钥库格式由PKCS#12转换为JKS。
检索新生成的密钥库文件,提取别名信息。
由密钥库文件导出数字证书(这里将用到别名)。
通过代码提取公钥/私钥、签名算法等

先看格式转换:
Cmd代码  
echo 格式转换  
keytool -importkeystore -v  -srckeystore zlex.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore zlex.keystore -deststoretype jks -deststorepass 123456  

-importkeystore导入密钥库,通过格式设定,我们可以将PKCS#12文件转换为JKS格式。
-v显示详情
-srckeystore源密钥库,这里是zlex.pfx
-srcstoretype源密钥库格式,这里为pkcs12
-srcstorepass源密钥库密码,这里为123456
-destkeystore目标密钥库,这里为zlex.keystore
-deststoretype目标密钥库格式,这里为jks,默认值也如此
-deststorepass目标密钥库密码,这里为123456
通过这个操作,我们能够获得所需的密钥库文件zlex.keystore。


 
这时,我们已经获得了密钥库文件,只要确定对应的别名信息,就可以提取公钥/私钥,以及数字证书,进行加密交互了!
Cmd代码  
echo 查看证书  
keytool -list -keystore zlex.keystore -storepass 123456 -v  

-list列举密钥库
-keystore密钥库,这里是zlex.keystore
-storepass密钥库密码,这里是123456
-v显示详情


 
这里需要细致观察一下别名信息!!!就是红框中的数字1!!!


现在,我们把证书导出!
Cmd代码  
echo 导出证书  
keytool -exportcert -alias 1 -keystore zlex.keystore -file zlex.crt -storepass 123456  

-exportcert导出证书
-alias别名,这里是1
-keystore密钥库,这里是zlex.keystore
-file证书文件,这里是zlex.crt
-storepass密钥库密码,这里是123456


 
现在证书也导出了,我们可以提取公钥/私钥,进行加密/解密,签名/验证操作了!当然,即便没有证书,我们也能够通过密钥库(JKS格式)文件获得证书,以及公钥/私钥、签名算法等。
补充代码, 其实就是对Java加密技术(八)的修改!
Java代码  
/**
 * 2010-8-11
 */  
 
import java.io.FileInputStream;  
import java.security.KeyStore;  
import java.security.PrivateKey;  
import java.security.PublicKey;  
import java.security.Signature;  
import java.security.cert.Certificate;  
import java.security.cert.CertificateFactory;  
import java.security.cert.X509Certificate;  
import java.util.Date;  
 
import javax.crypto.Cipher;  
 
/**
 * 证书操作类
 *  
 * @author 梁栋
 * @since 1.0
 */  
public class CertificateCoder {  
    /**
     * Java密钥库(Java Key Store,JKS)KEY_STORE
     */  
    public static final String KEY_STORE = "JKS";  
 
    public static final String X509 = "X.509";  
 
    /**
     * 由 KeyStore获得私钥
     *  
     * @param keyStorePath
     * @param keyStorePassword
     * @param alias
     * @param aliasPassword
     * @return
     * @throws Exception
     */  
    private static PrivateKey getPrivateKey(String keyStorePath,  
            String keyStorePassword, String alias, String aliasPassword)  
            throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
        PrivateKey key = (PrivateKey) ks.getKey(alias,  
                aliasPassword.toCharArray());  
        return key;  
    }  
 
    /**
     * 由 Certificate获得公钥
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static PublicKey getPublicKey(String certificatePath)  
            throws Exception {  
        Certificate certificate = getCertificate(certificatePath);  
        PublicKey key = certificate.getPublicKey();  
        return key;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String certificatePath)  
            throws Exception {  
        CertificateFactory certificateFactory = CertificateFactory  
                .getInstance(X509);  
        FileInputStream in = new FileInputStream(certificatePath);  
 
        Certificate certificate = certificateFactory.generateCertificate(in);  
        in.close();  
 
        return certificate;  
    }  
 
    /**
     * 获得Certificate
     *  
     * @param keyStorePath
     * @param keyStorePassword
     * @param alias
     * @return
     * @throws Exception
     */  
    private static Certificate getCertificate(String keyStorePath,  
            String keyStorePassword, String alias) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
        Certificate certificate = ks.getCertificate(alias);  
 
        return certificate;  
    }  
 
    /**
     * 获得KeyStore
     *  
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */  
    private static KeyStore getKeyStore(String keyStorePath, String password)  
            throws Exception {  
        FileInputStream is = new FileInputStream(keyStorePath);  
        KeyStore ks = KeyStore.getInstance(KEY_STORE);  
        ks.load(is, password.toCharArray());  
        is.close();  
        return ks;  
    }  
 
    /**
     * 私钥加密
     *  
     * @param data
     * @param keyStorePath
     * @param keyStorePassword
     * @param alias
     * @param aliasPassword
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
            String keyStorePassword, String alias, String aliasPassword)  
            throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 私钥解密
     *  
     * @param data
     * @param keyStorePath
     * @param alias
     * @param keyStorePassword
     * @param aliasPassword
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String keyStorePassword, String aliasPassword)  
            throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥加密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
 
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 公钥解密
     *  
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
 
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
 
        return cipher.doFinal(data);  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(String certificatePath) {  
        return verifyCertificate(new Date(), certificatePath);  
    }  
 
    /**
     * 验证Certificate是否过期或无效
     *  
     * @param date
     * @param certificatePath
     * @return
     */  
    public static boolean verifyCertificate(Date date, String certificatePath) {  
        boolean status = true;  
        try {  
            // 取得证书  
            Certificate certificate = getCertificate(certificatePath);  
            // 验证证书是否过期或无效  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证证书是否过期或无效
     *  
     * @param date
     * @param certificate
     * @return
     */  
    private static boolean verifyCertificate(Date date, Certificate certificate) {  
        boolean status = true;  
        try {  
            X509Certificate x509Certificate = (X509Certificate) certificate;  
            x509Certificate.checkValidity(date);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 签名
     *  
     * @param keyStorePath
     * @param alias
     * @param keyStorePassword
     * @param aliasPassword
     * @return
     * @throws Exception
     */  
    public static byte[] sign(byte[] sign, String keyStorePath, String alias,  
            String keyStorePassword, String aliasPassword) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(  
                keyStorePath, keyStorePassword, alias);  
 
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
 
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);  
        return signature.sign();  
    }  
 
    /**
     * 验证签名
     *  
     * @param data
     * @param sign
     * @param certificatePath
     * @return
     * @throws Exception
     */  
    public static boolean verify(byte[] data, byte[] sign,  
            String certificatePath) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
 
        return signature.verify(sign);  
 
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param keyStorePassword
     * @param alias
     * @return
     */  
    public static boolean verifyCertificate(Date date, String keyStorePath,  
            String keyStorePassword, String alias) {  
        boolean status = true;  
        try {  
            Certificate certificate = getCertificate(keyStorePath,  
                    keyStorePassword, alias);  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
 
    /**
     * 验证Certificate
     *  
     * @param keyStorePath
     * @param keyStorePassword
     * @param alias
     * @return
     */  
    public static boolean verifyCertificate(String keyStorePath,  
            String keyStorePassword, String alias) {  
        return verifyCertificate(new Date(), keyStorePath, keyStorePassword,  
                alias);  
    }  
}  

相信上述代码已经帮朋友们解决了相当多的问题!
给出测试类:
Java代码  
import static org.junit.Assert.*;  
 
import java.util.Date;  
 
import org.apache.commons.codec.binary.Hex;  
import org.junit.Test;  
 
/**
 * 证书操作验证类
 *  
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */  
public class CertificateCoderTest {  
    private String certificatePath = "zlex.crt";  
    private String keyStorePath = "zlex.keystore";  
    private String keyStorePassword = "123456";  
    private String aliasPassword = "123456";  
    private String alias = "1";  
 
    @Test  
    public void test() throws Exception {  
        System.err.println("公钥加密——私钥解密");  
        String inputStr = "Ceritifcate";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encrypt = CertificateCoder.encryptByPublicKey(data,  
                certificatePath);  
 
        byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,  
                keyStorePath, alias, keyStorePassword, aliasPassword);  
        String outputStr = new String(decrypt);  
 
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
 
        // 验证数据一致  
        assertArrayEquals(data, decrypt);  
 
        // 验证证书有效  
        assertTrue(CertificateCoder.verifyCertificate(certificatePath));  
 
    }  
 
    @Test  
    public void testSign() throws Exception {  
        System.err.println("私钥加密——公钥解密");  
 
        String inputStr = "sign";  
        byte[] data = inputStr.getBytes();  
 
        byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,  
                keyStorePath, keyStorePassword, alias, aliasPassword);  
 
        byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,  
                certificatePath);  
 
        String outputStr = new String(decodedData);  
        System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);  
        assertEquals(inputStr, outputStr);  
 
        System.err.println("私钥签名——公钥验证签名");  
        // 产生签名  
        byte[] sign = CertificateCoder.sign(encodedData, keyStorePath, alias,  
                keyStorePassword, aliasPassword);  
        System.err.println("签名:\r" + Hex.encodeHexString(sign));  
 
        // 验证签名  
        boolean status = CertificateCoder.verify(encodedData, sign,  
                certificatePath);  
        System.err.println("状态:\r" + status);  
        assertTrue(status);  
    }  
 
    @Test  
    public void testVerify() throws Exception {  
        System.err.println("密钥库证书有效期验证");  
        boolean status = CertificateCoder.verifyCertificate(new Date(),  
                keyStorePath, keyStorePassword, alias);  
        System.err.println("证书状态:\r" + status);  
        assertTrue(status);  
    }  
}  

第一个测试方法,用于提取公钥/私钥进行加密/解密操作。
第二个测试方法,用于提取签名算法进行签名/验证操作。
第三个测试方法,用于测试密钥库该别名对应的证书,当前日期下,是否有效。
 


OK,任务完成,密钥成功提取,剩下的都是代码基本功了!

你可能感兴趣的:(java,开发人员)