Java&keytool生成RSA**
工作中需要使用非对称加密RSA来进行消息摘要生产和验证,但无法通过keytool工具来提取私钥。
那怎么获得私钥、公钥?
以Java为例:通过KeyStore类getEntry() 或者getKey()来提取私钥;通过Certificate类getPublicKey()获取公钥。
一.Keytool生成KeyStore文件
Cmd代码
- -- 生成密码仓库test.store
- eytool -genkey -v -alias test -dname "CN=test,OU=HE,O=CUI,L=SHENGZHEN,ST=GUANGDONG,C=CN" -keyalg RSA -keysize 2048 -keypass 5201314 -keystore test.store -storepass 5201314 -validity 10000 -storetype JCEKS
- -- 导出证书test.crt
- eytool -exportcert -alias test -file test.crt -keystore test.store -storepass 5201314 -rfc -storetype JCEKS
运行后在F盘的Key目录下将产生如下两个文件。
二.生成私钥、公钥
KeyStoreHelper.java
Java代码
- package com.bijian.keystore;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.InputStream;
- import java.io.ObjectOutputStream;
- import java.security.KeyStore;
- import java.security.PrivateKey;
- import java.security.PublicKey;
- import java.security.cert.Certificate;
- import java.security.cert.CertificateException;
- import java.security.cert.CertificateFactory;
- import sun.misc.BASE64Encoder;
- public class KeyStoreHelper {
- public static void main(String[] args) throws Exception {
- String privatePath = "F:/Key/testPri.key"; // 准备导出的私钥
- String publicPath = "F:/Key/testPub.key"; // 准备导出的公钥
- PrivateKey privateKey = getPrivateKeyFromStore();
- createKeyFile(privateKey, privatePath);
- PublicKey publicKey = getPublicKeyFromCrt();
- createKeyFile(publicKey, publicPath);
- byte[] publicKeyBytes = publicKey.getEncoded();
- byte[] privateKeyBytes = privateKey.getEncoded();
- String publicKeyBase64 = new BASE64Encoder().encode(publicKeyBytes);
- String privateKeyBase64 = new BASE64Encoder().encode(privateKeyBytes);
- System.out.println("publicKeyBase64.length():" + publicKeyBase64.length());
- System.out.println("publicKeyBase64:" + publicKeyBase64);
- System.out.println("privateKeyBase64.length():" + privateKeyBase64.length());
- System.out.println("privateKeyBase64:" + privateKeyBase64);
- }
- private static PrivateKey getPrivateKeyFromStore() throws Exception {
- String alias = "test"; // KeyTool中生成KeyStore时设置的alias
- String storeType = "JCEKS"; // KeyTool中生成KeyStore时设置的storetype
- char[] pw = "5201314".toCharArray(); // KeyTool中生成KeyStore时设置的storepass
- String storePath = "F:/Key/test.store"; // KeyTool中已生成的KeyStore文件
- storeType = null == storeType ? KeyStore.getDefaultType() : storeType;
- KeyStore keyStore = KeyStore.getInstance(storeType);
- InputStream is = new FileInputStream(storePath);
- keyStore.load(is, pw);
- // 由**库获取**的两种方式
- // KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(pw));
- // return pkEntry.getPrivateKey();
- return (PrivateKey) keyStore.getKey(alias, pw);
- }
- private static PublicKey getPublicKeyFromCrt() throws CertificateException, FileNotFoundException {
- String crtPath = "F:/Key/test.crt"; // KeyTool中已生成的证书文件
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- FileInputStream in = new FileInputStream(crtPath);
- Certificate crt = cf.generateCertificate(in);
- PublicKey publicKey = crt.getPublicKey();
- return publicKey;
- }
- private static void createKeyFile(Object key, String filePath) throws Exception {
- FileOutputStream fos = new FileOutputStream(filePath);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(key);
- oos.flush();
- oos.close();
- }
- }
运行上面的代码后,将在F盘的Key目录下新产生testPub.key、testPri.key公私钥文件。
并在控制台输出如下内容:
到此为止,公私钥已生成。
三.验证上面生成的公私钥
1.私钥加签公钥验签,进行验证
DigestUtil.java
Java代码
- package com.bijian.keystore;
- public class DigestUtil {
- /**
- * 字节转为十六进制字符串
- * @param字节
- * @return 十六进制字符串
- */
- public static String byte2hex(byte[] b) {
- String hs = "";
- String stmp = "";
- for (int n = 0; b != null && n < b.length; n++) {
- stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
- if (stmp.length() == 1)
- hs = hs + "0" + stmp;
- else
- hs = hs + stmp;
- }
- return hs;
- }
- /**
- * 十六进制字符转为字节
- * @param 十六进制字符
- * @return 字节
- */
- public static byte[] hex2byte(byte[] b) {
- if ((b.length % 2) != 0)
- throw new IllegalArgumentException("byte length is not correct");
- byte[] b2 = new byte[b.length / 2];
- for (int n = 0; n < b.length; n += 2) {
- String item = new String(b, n, 2);
- b2[n / 2] = (byte) Integer.parseInt(item, 16);
- }
- return b2;
- }
- /**
- * 字符串转换成十六进制值
- * @param bin String 我们看到的要转换成十六进制的字符串
- * @return
- */
- public static String bin2hex(String bin) {
- char[] digital = "0123456789ABCDEF".toCharArray();
- StringBuffer sb = new StringBuffer("");
- byte[] bs = bin.getBytes();
- int bit;
- for (int i = 0; i < bs.length; i++) {
- bit = (bs[i] & 0x0f0) >> 4;
- sb.append(digital[bit]);
- bit = bs[i] & 0x0f;
- sb.append(digital[bit]);
- }
- return sb.toString();
- }
- }
SignUtil.java
Java代码
- package com.bijian.keystore;
- import java.security.KeyFactory;
- import java.security.PrivateKey;
- import java.security.spec.PKCS8EncodedKeySpec;
- import org.apache.commons.codec.binary.Base64;
- public class SignUtil {
- //#priKeyText
- private final static String priKeyText = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQChXmxaPt6Qg+XTiqaCG1cZEKwGPbKy/Qs5xGNx4HotngGUM2n/5FdZtSHZofJWsNUUKpZa+BynhNg0E0Qn4Xrp0WSi+Z5LgDewT7DeA0b5Cky386MEWwc11Asa+SuMiR5XFUjXdWrMQX5E7wVRcbuoq7A8QBfm3i8F4PdMokhlMviwhLngKOrWVKobE8cyA25Jv2FZgxv1NzVZ7zyGaO60a49X/NaA2poe5OB9zXBfsa8kfHl0b+sMQMd38uVrtCIqs2KABW6EGTToezk55i+hHg7nqMLum7Xtw8z/T1fpEtKgsnKANph7eqRHVHmjUDpHidx3BiTAArw07MVIq1LhAgMBAAECggEADJPDdjU4O6NMInTIDZP78eQuxD3C09iNK293IMUSQMPz840eUeeGN2O6w6+vp7oYoX3AQk7cTOI5x7VItqMIZXkAkwNJpzDTJlbPvj4bJgX7fMrshcZihXuFchDBqC53wunRx5lLPahNIypOC88FhVv8XHXSZxgiKh8ip0JuyhRS38TDVCcrIzKclsSY9CT3kYhVdPkfWEEGSb0qj7J4VxK7evE6yQD0/UJqB9j54Ts418DsEHNOZPDf6PFgNgl585AQZ3KpEUrfsx3rygLhUG+GQD+bkkDExA7vJlw5v1CF7Kr1T/XK9gjqRbRBjtzLy0MUe11ips3OM5LYkNv/EQKBgQDuWD+i64alAdXeReJAHWU8sQjZgmZYhlMhxfq8rsalJ490br3joZ2+KHXD7yDmvTUj0IFre99BcB7hi9Sx0joOjyS8+Rz7Bw02bdnwJbva5SYg3XojqdEoKGl5nFN67rL762jGsDG2Y5/hVEe/vxmH9sWB/OvQZ10If3p5OJ/jcwKBgQCtUnl1IWn57xHzqDqQEIMlCskGRXElMYdU/jtADLTt27DgSjzbgW5GgGMectvDDxYADYb93VUuTvz4EvHXAwo3CepDUjs4JtkGZeaAnMCER6doIyXPfRsxHMXRN4NgG70yEA5u7IfYFA1DuGLDwjddg4rU122ftqUM2UcwaaBjWwKBgQC9R9wRuFXPiOudf4Y0QKP7VOSgSAybVOGEOsPrQCmFUyt73c5zjg/Fyj/sAGXymGQxMw70mwUr5KzBldit9zQgB9G3OWaofGsjxI2FR5IuPjjPdNPgqqXt7FoHN/yb7iC6K7Ojxp1UKT35JoNsZYkTDwi/OGrVsKCTdRmAV1WyvQKBgBZD6Qxt/XI5DwJREyzcoixJBWgD1bQkd7Eoc64Xs8p2lXNKtiSwrNzrs0//C1I0huv80OGd5EptpTutG1o2rsJBSNHbJ3ZgLzMONh1Bhc24cr4C/eF4vdyCSLtGuV7IUXaz71a6lfzhHo8bibyCH6CovFX5UsDYsr1C0E1c1FjPAoGAXQ4hZ7/APQ/V1wmkZnplGchbXefqixvhDZWUvIAy6sAmypaRe3fRjd3SFLGXhOg48CQ59A0P3dQjKja8U7eQA6c6qw9Ci4F/cceHcdgnH6fOkqiHMdN6Sr0/SinbVP0kqU25y+AbRdJjwYZGTmWeqMdFUra+MVK9befn+hA3zAk=";
- //#pubKeyText
- private final static String pubKeyText = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoV5sWj7ekIPl04qmghtXGRCsBj2ysv0LOcRjceB6LZ4BlDNp/+RXWbUh2aHyVrDVFCqWWvgcp4TYNBNEJ+F66dFkovmeS4A3sE+w3gNG+QpMt/OjBFsHNdQLGvkrjIkeVxVI13VqzEF+RO8FUXG7qKuwPEAX5t4vBeD3TKJIZTL4sIS54Cjq1lSqGxPHMgNuSb9hWYMb9Tc1We88hmjutGuPV/zWgNqaHuTgfc1wX7GvJHx5dG/rDEDHd/Lla7QiKrNigAVuhBk06Hs5OeYvoR4O56jC7pu17cPM/09X6RLSoLJygDaYe3qkR1R5o1A6R4ncdwYkwAK8NOzFSKtS4QIDAQAB";
- private final static String CHARACTER_ENCODING_UTF_8 = "UTF-8";
- public static void main(String[] args) {
- String signString = "bijian 您好!";
- try {
- // 加签
- String localSignature = SignUtil.sign(priKeyText.getBytes(CHARACTER_ENCODING_UTF_8), signString);
- System.out.println(localSignature);
- //验签
- boolean verifyResult = SignUtil.verify(pubKeyText.getBytes(CHARACTER_ENCODING_UTF_8), signString, localSignature);
- System.out.println("verifyResult:" + verifyResult);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * RSA私钥加签
- * @param priKeyText经过base64处理后的私钥
- * @param plainText明文内容
- * @return 十六进制的签名字符串
- * @throws Exception
- */
- public static String sign(byte[] priKeyText, String plainText) throws Exception {
- try {
- PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(priKeyText));
- KeyFactory keyf = KeyFactory.getInstance("RSA");
- PrivateKey prikey = keyf.generatePrivate(priPKCS8);
- // 用私钥对信息生成数字签名
- java.security.Signature signet = java.security.Signature.getInstance("SHA256withRSA");
- signet.initSign(prikey);
- signet.update(plainText.getBytes("UTF-8"));
- return DigestUtil.byte2hex(signet.sign());
- } catch (Exception e) {
- throw e;
- }
- }
- /**
- * 公钥验签
- * @param pubKeyText经过base64处理后的公钥
- * @param plainText明文内容
- * @param signText十六进制的签名字符串
- * @return 验签结果 true验证一致 false验证不一致
- */
- public static boolean verify(byte[] pubKeyText, String plainText, String signText) {
- try {
- // 解密由base64编码的公钥,并构造X509EncodedKeySpec对象
- java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
- Base64.decodeBase64(pubKeyText));
- // RSA算法
- java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
- // 取公钥匙对象
- java.security.PublicKey pubKey = keyFactory.generatePublic(bobPubKeySpec);
- // 十六进制数字签名转为字节
- byte[] signed = DigestUtil.hex2byte(signText.getBytes("UTF-8"));
- java.security.Signature signatureChecker = java.security.Signature.getInstance("SHA256withRSA");
- signatureChecker.initVerify(pubKey);
- signatureChecker.update(plainText.getBytes("UTF-8"));
- // 验证签名是否正常
- return signatureChecker.verify(signed);
- } catch (Throwable e) {
- return false;
- }
- }
- }
运行结果如下,验签通过。
2.将F:\Key下生成公私钥验证加解密
将F:\Key下的testPub.key、testPri.key拷贝至工程中
运行如下代码
Java代码
- package com.bijian.keystore;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.security.Key;
- import javax.crypto.Cipher;
- import sun.misc.BASE64Decoder;
- import sun.misc.BASE64Encoder;
- public class RSAUtil2 {
- /** 指定加密算法为RSA */
- private static final String ALGORITHM = "RSA";
- /** 指定公钥存放文件 */
- private static String PUBLIC_KEY_FILE = "testPub.key";
- /** 指定私钥存放文件 */
- private static String PRIVATE_KEY_FILE = "testPri.key";
- public static void main(String[] args) throws Exception {
- String source = "深圳,你好!";// 要加密的字符串
- System.out.println("准备用公钥加密的字符串为:" + source);
- String cryptograph = encrypt(source);// 生成的密文
- System.out.print("用公钥加密后的结果为:" + cryptograph);
- System.out.println();
- String target = decrypt(cryptograph);// 解密密文
- System.out.println("用私钥解密后的字符串为:" + target);
- System.out.println();
- }
- /**
- * 加密方法
- * @param source 源数据
- * @return
- * @throws Exception
- */
- public static String encrypt(String source) throws Exception {
- Key publicKey = getKey(PUBLIC_KEY_FILE);
- /** 得到Cipher对象来实现对源数据的RSA加密 */
- Cipher cipher = Cipher.getInstance(ALGORITHM);
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- byte[] b = source.getBytes();
- /** 执行加密操作 */
- byte[] b1 = cipher.doFinal(b);
- BASE64Encoder encoder = new BASE64Encoder();
- return encoder.encode(b1);
- }
- /**
- * 解密算法
- * @param cryptograph 密文
- * @return
- * @throws Exception
- */
- public static String decrypt(String cryptograph) throws Exception {
- Key privateKey = getKey(PRIVATE_KEY_FILE);
- /** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
- Cipher cipher = Cipher.getInstance(ALGORITHM);
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- BASE64Decoder decoder = new BASE64Decoder();
- byte[] b1 = decoder.decodeBuffer(cryptograph);
- /** 执行解密操作 */
- byte[] b = cipher.doFinal(b1);
- return new String(b);
- }
- private static Key getKey(String fileName) throws Exception, IOException {
- Key key;
- ObjectInputStream ois = null;
- try {
- /** 将文件中的私钥对象读出 */
- ois = new ObjectInputStream(new FileInputStream(fileName));
- key = (Key) ois.readObject();
- } catch (Exception e) {
- throw e;
- } finally {
- ois.close();
- }
- return key;
- }
- }
运行结果:
附:sun.misc.BASE64Encoder找不到jar包的解决方法
在MyEclipse中编写Java代码时,用到了BASE64Decoder,import sun.misc.BASE64Decoder;可是Eclipse提示:
Access restriction: The type BASE64Decoder is not accessible due to restriction on required library C:\Program
files\java\jre6\lib\rt.jar
Access restriction : The constructor BASE64Decoder() is not accessible due to restriction on required library C:\Program files\java\jre6\lib\rt.jar
解决方案1(推荐):
只需要在project build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
解决方案2:
右键项目->属性->Java bulid path->jre System Library->access rules->resolution选择accessible,下面填上**,点击确定即可!!!
参考文章:http://jingyan.baidu.com/article/f3e34a12ad7acff5ea653569.html
附阿里生成公私钥的链接:https://docs.open.alipay.com/291/105971