以下是CA證書的工具類
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Encoder;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertAndKeyGen;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X500Signer;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import com.inner.game.util.BASE64;
/**
* 首先生成CA的根證書,然後有CA的根證書籤署生成linkSDK001的證書
*
* @author pfx
*/
public class GenX509Cert {
/** 提供強加密隨機數生成器 (RNG)* */
private SecureRandom sr;
private String userprivKey;
private String publickey;
private String serPublicKey;
private String serPrivateKey;
private String serCert;
static {
try {
Security.addProvider(new BouncyCastleProvider());
} catch (Exception e) {
e.printStackTrace();
}
}
public GenX509Cert() throws NoSuchAlgorithmException,
NoSuchProviderException {
// 返回實現指定隨機數生成器 (RNG) 算法的 SecureRandom 對象。
sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
}
/**
* 生成證書
*
* @param certificate
* @param rootPrivKey
* @param kp
* @param subInfo
* @param subAlias
* @param subPwd
* @param subFilepath
* @return
* @throws CertificateException
* @throws IOException
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws SignatureException
*/
public boolean createCert(X509Certificate certificate,
PrivateKey rootPrivKey, KeyPair kp, String subInfo,
String subAlias, String subPwd, String subFilepath, String filename)
throws CertificateException, IOException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException,
SignatureException {
// X.509 v1 證書的抽象類。此類提供了一種訪問 X.509 v1 證書所有屬性的標準方式。
byte certbytes[] = certificate.getEncoded();
// The X509CertImpl class represents an X.509 certificate.
X509CertImpl x509certimpl = new X509CertImpl(certbytes);
// The X509CertInfo class represents X.509 certificate information.
X509CertInfo x509certinfo = (X509CertInfo) x509certimpl
.get("x509.info");
// This class defines the X509Key attribute for the Certificate.
x509certinfo.set("key", new CertificateX509Key(kp.getPublic()));
// 設置issuer域 "CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN"
X500Name issuer = new X500Name(certificate.getIssuerDN().getName());
x509certinfo.set("issuer.dname", issuer);
// 2253 style). "CN=scriptx, OU=wps, O=wps, L=BJ, ST=BJ, C=CN"
X500Name subject = new X500Name(subInfo);
x509certinfo.set("subject.dname", subject);
// 此 Signature 類用來爲應用程序提供數字簽名算法功能。返回實現指定簽名算法的 Signature 對象。
Signature signature = Signature.getInstance("MD5WithRSA");
// 初始化這個用於簽名的對象。如果使用其他參數再次調用此方法,此調用的結果將無效。
signature.initSign(kp.getPrivate());
// This class provides a binding between a Signature object and an
// authenticated X.500 name (from an X.509 certificate chain), which is
// needed in many public key signing applications.
X500Signer signer = new X500Signer(signature, issuer);
// This class identifies algorithms, such as cryptographic transforms,
// each of which may be associated with parameters.
AlgorithmId algorithmid = signer.getAlgorithmId();
// This class defines the AlgorithmId for the Certificate.
x509certinfo
.set("algorithmID", new CertificateAlgorithmId(algorithmid));
// 開始時間
Date bdate = new Date();
// 結束時間
Date edate = new Date();
// 天 小時 分 秒 毫秒
edate.setTime(bdate.getTime() + 3650 * 24L * 60L * 60L * 1000L);
// validity爲有效時間長度 單位爲秒,This class defines the interval for which the
// certificate is valid.證書的有效時間
CertificateValidity certificatevalidity = new CertificateValidity(
bdate, edate);
x509certinfo.set("validity", certificatevalidity);
// This class defines the SerialNumber attribute for the Certificate.
// 設置有效期域(包含開始時間和到期時間)域名等同與x509certinfo.VALIDITY
x509certinfo.set("serialNumber", new CertificateSerialNumber(
(int) (new Date().getTime() / 1000L)));
// 設置序列號域,This class defines the version of the X509 Certificate.
CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);
x509certinfo.set(X509CertInfo.VERSION, cv);
// 設置版本號 只有v1 ,v2,v3這幾個合法值
X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);
x509certimpl1.sign(rootPrivKey, "MD5WithRSA");
// 使用另一個證書的私鑰來簽名此證書 這裏使用 md5散列 用rsa來加密
// BASE64Encoder base64 = new BASE64Encoder();
// FileOutputStream fos = new FileOutputStream(new File(subFilepath
// + filename + ".crt"));
// // base64.encodeBuffer(x509certimpl1.getEncoded(), fos);
// String str = "-----BEGIN CERTIFICATE-----\r\n"
// + base64.encode(x509certimpl1.getEncoded())
// + "\r\n-----END CERTIFICATE-----";
// fos.write(str.getBytes());
// fos.flush();
// fos.close();
try {
Certificate[] certChain = { x509certimpl1 };
// savePfx("scriptx", kp.getPrivate(), "123456", certChain,
// "C:\\lzj\\tmp\\cer\\ca\\ScriptX.jks");
savePfx(subAlias, kp.getPrivate(), subPwd, certChain, subFilepath
+ filename + ".pfx");
// saveJks(subAlias, kp.getPrivate(), subPwd, certChain, subFilepath
// + filename + ".jks");
// FileInputStream in = new FileInputStream(subFilepath + filename
// + ".jks");
FileInputStream in = new FileInputStream(subFilepath + filename
+ ".pfx");
FileInputStream newin = new FileInputStream(subFilepath + filename
+ ".pfx");
// FileInputStream newin = new FileInputStream(subFilepath +
// filename
// + ".jks");
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[newin.available()];
newin.read(buffer);
out.write(buffer);
// KeyStore inputKeyStore = KeyStore.getInstance("jks");
KeyStore inputKeyStore = KeyStore.getInstance("pkcs12", "BC");
inputKeyStore.getProvider().getName();
inputKeyStore.load(in, subPwd.toCharArray());
PrivateKey privk = (PrivateKey) inputKeyStore.getKey(subAlias,
subPwd.toCharArray());
String key = BASE64.encode(privk.getEncoded());
// FileOutputStream privKfos = new FileOutputStream(new File(
// subFilepath + filename + ".pvk"));
//
// privKfos.write(privk.getEncoded());
userprivKey = BASE64.encode(buffer);
publickey = BASE64
.encode(x509certimpl1.getPublicKey().getEncoded());
System.out.println("publickey = " + publickey);
System.out.println("userprivKey = " + userprivKey);
System.out.println("key = " + key);
// System.out.println("userprivKey = " + new String(buffer));
// base64.encode(key.getEncoded(), privKfos);
newin.close();
in.close();
// 生成文件
x509certimpl1.verify(certificate.getPublicKey(), null);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 保存此根證書信息KeyStore Personal Information Exchange
*
* @param alias
* @param privKey
* @param pwd
* @param certChain
* @param filepath
* @throws Exception
*/
public void savePfx(String alias, PrivateKey privKey, String pwd,
Certificate[] certChain, String filepath) throws Exception {
Security
.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyStore outputKeyStore = KeyStore.getInstance("pkcs12", "BC");
outputKeyStore.load(null, pwd.toCharArray());
System.out.println(outputKeyStore.getProvider().getInfo());
outputKeyStore
.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);
FileOutputStream out = new FileOutputStream(filepath);
// 將此 keystore 存儲到給定輸出流,並用給定密碼保護其完整性。
System.out.println(outputKeyStore.getKey(alias, pwd.toCharArray()));
outputKeyStore.store(out, pwd.toCharArray());
out.close();
}
/**
* 生成jks文件
*
* @param alias
* @param privKey
* @param pwd
* @param certChain
* @param filepath
* @throws Exception
*/
public void saveJks(String alias, PrivateKey privKey, String pwd,
Certificate[] certChain, String filepath) throws Exception {
KeyStore outputKeyStore = KeyStore.getInstance("jks");
outputKeyStore.load(null, pwd.toCharArray());
outputKeyStore
.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);
FileOutputStream out = new FileOutputStream(filepath);
outputKeyStore.store(out, pwd.toCharArray());
out.close();
}
/**
* 頒佈根證書,自己作爲CA
*
* @param subInfo
* @param alias
* @param pwd
* @param filepath
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidKeyException
* @throws IOException
* @throws CertificateException
* @throws SignatureException
* @throws UnrecoverableKeyException
*/
public void createRootCA(String caInfo, String alias, String pwd,
String filepath) throws NoSuchAlgorithmException,
NoSuchProviderException, InvalidKeyException, IOException,
CertificateException, SignatureException, UnrecoverableKeyException {
CertAndKeyGen cak = new CertAndKeyGen("RSA", "MD5WithRSA", null);
cak.setRandom(sr);
cak.generate(1024);
BASE64Encoder base64 = new BASE64Encoder();
// byte[] key1 = cak.getPublicKey().getEncoded();
// String pri1 = BASE64.encode(key1);
// "CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN"
X500Name subject = new X500Name(caInfo);
// 自簽名的根證書
X509Certificate certificate = cak.getSelfCertificate(subject,
3650 * 24L * 60L * 60L);
// X509Certificate certificate = cak.getSelfCertificate(subject,
// new Date(), 3650 * 24L * 60L * 60L);
X509Certificate[] certs = { certificate };
try {
// savePfx("RootCA", cak.getPrivateKey(), "123456", certs,
// "C:\\lzj\\tmp\\cer\\ca\\RootCa.pfx");
saveJks(alias, cak.getPrivateKey(), pwd, certs, filepath
+ "RootCa.jks");
} catch (Exception e) {
e.printStackTrace();
return;
}
// 後一個long型參數代表從現在開始的有效期 單位爲秒(如果不想從現在開始算 可以在後面改這個域)
FileOutputStream fos = new FileOutputStream(new File(filepath
+ "RootCa.crt"));
// 添加證書前和後標記
// base64.encodeBuffer(certificate.getEncoded(), fos);
String str = "-----BEGIN CERTIFICATE-----\r\n"
+ base64.encode(certificate.getEncoded())
+ "\r\n-----END CERTIFICATE-----";
// System.out.println("ca證書:start------" );
serCert = base64.encode(certificate.getEncoded());
// System.out.println( base64.encode(certificate.getEncoded()));
// System.out.println("ca證書:end------" );
fos.write(str.getBytes());
fos.flush();
fos.close();
serPrivateKey = BASE64.encode(cak.getPrivateKey().getEncoded());
serPublicKey = BASE64.encode(certificate.getPublicKey().getEncoded());
System.out.println("serPrivateKey = " + serPrivateKey);
System.out.println("serPublicKey = " + serPublicKey);
}
public boolean signCert(String alias, String pwd, String filepath,
String subInfo, String subAlias, String subPwd, String subFilepath,
String filename) throws NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException,
InvalidKeyException, NoSuchProviderException, SignatureException {
try {
// KeyStore ks = KeyStore.getInstance("pkcs12");
// FileInputStream ksfis = new
// FileInputStream("C:\\lzj\\tmp\\cer\\ca\\RootCa.pfx");
KeyStore ks = KeyStore.getInstance("jks");
FileInputStream ksfis = new FileInputStream(filepath + "RootCa.jks");
char[] storePwd = pwd.toCharArray();
char[] keyPwd = pwd.toCharArray();
// 從給定輸入流中加載此 KeyStore。
ks.load(ksfis, storePwd);
ksfis.close();
// 返回與給定別名關聯的密鑰(私鑰),並用給定密碼來恢復它。必須已經通過調用 setKeyEntry,或者以
// PrivateKeyEntry
// 或 SecretKeyEntry 爲參數的 setEntry 關聯密鑰與別名。
PrivateKey privK = (PrivateKey) ks.getKey(alias, keyPwd);
// 返回與給定別名關聯的證書。如果給定的別名標識通過調用 setCertificateEntry 創建的條目,或者通過調用以
// TrustedCertificateEntry 爲參數的 setEntry
// 創建的條目,則返回包含在該條目中的可信證書。如果給定的別名標識通過調用 setKeyEntry 創建的條目,或者通過調用以
// PrivateKeyEntry 爲參數的 setEntry 創建的條目,則返回該條目中證書鏈的第一個元素。
X509Certificate certificate = (X509Certificate) ks
.getCertificate(alias);
return createCert(certificate, privK, genKey(), subInfo, subAlias,
subPwd, subFilepath, filename);
} catch (KeyStoreException e) {
e.printStackTrace();
return false;
}
}
public KeyPair genKey() throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024, sr);
KeyPair kp = kpg.generateKeyPair();
return kp;
}
// public static void main(String[] args) {
//
// try {
// GenX509Cert gcert = new GenX509Cert();
// /*
// * 創建ca證書,該操作只需只需一次,執行條件是更換服務器證書時,有效期10年。
// *
// * 運行環境:jdk1.6
// *
// * 參數說明: caInfo --指定證書擁有者信息,本例中可以不用修改,分別表示: ? OU(Organization Unit -
// * 組織單位名稱) ? O(Organization - 組織名稱) ? L(Locality - 城市或區域名稱) ?
// * ST(State - 州或省份名稱) ? C(Country - 國家名稱) caAlias
// * --產生別名,用RootCA就可以,不用變。 caPwd --指定別名條目的密碼,本例中該密碼同時也是jks密碼
// * caFilepath --證書保存路徑,運行後將在該路徑中生成【RootCa.jks】【RootCa.crt】兩個文件,
// * 文件說明: 其中【RootCa.jks】用於部署應用服務器的https服務時用
// * 其中【RootCa.crt】文件用於預埋在客戶端中,驗證服務器證書用。
// */
// String caInfo = "CN=RootCA,OU=Linktech,O=Link,L=SY,S=LN,C=CN";
// String caAlias = "RootCA";
// String caPwd = "Link@1234";
// // String caFilepath = "C:\\lzj\\tmp\\cer\\ca\\ca2\\";
// String caFilepath = "D:/linshi/";
// // gcert.createRootCA(caInfo, caAlias, caPwd, caFilepath);
//
// // System.out.println("createRootCA完畢");
//
// /*
// * 生成用戶證書,該操作是用來申請用戶證書時用,每次申請證書時調用1次,該證書有效期10年,根據需要可以調整。
// *
// * 運行環境:jdk1.6
// *
// * 參數說明: subInfo --指定證書擁有者信息,含義同上。本例中用手機號表示相關信息 subAlias
// * --產生別名,建議用用戶手機號 subPwd --指定別名條目的密碼,本例中該密碼同時也是jks密碼,該密需要通知客戶端程序。
// * subFilepath
// * --證書保存路徑,運行後將在該路徑中生成【15940163439.jks】【15940163439.crt】
// * 【15940163439.pvk】三個文件, 文件說明:
// * 【15940163439.jks】密鑰倉庫,該文件可以根據需要傳給客戶端程序,客戶端需要利用subPwd這個密碼導出私鑰,
// * 並利用私鑰對數據進行簽名。 【15940163439.crt】本例中暫時無用處
// * 【15940163439.pvk】私鑰文件,該文件可以根據需要傳給客戶端程序,客戶端可以利用該文件對數據進行簽名。
// *
// * 上述文件主要是針對java生成的jks(keystore),如果客戶端想要pkcs12格式證書,需要修改部分程序實現。
// */
// String subInfo = "CN=15940163439,OU=user,O=SDK,L=SY,S=LN,C=CN";
// String subAlias = "15940163439";
// String subPwd = "15940163439";
// gcert.getUserCert(subInfo, subAlias);
// // String subFilepath = "C:\\lzj\\tmp\\cer\\ca\\ca2\\";
// String subFilepath = "D:/linshi/";
//
// // gcert.signCert(caAlias, caPwd, caFilepath, subInfo, subAlias,
// // subPwd, subFilepath);
// System.out.println("signCert完畢");
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
public static void main(String[] args) throws Exception {
GenX509Cert gen = new GenX509Cert();
String serverCert = gen.getServerCert();
System.out.println(serverCert);
// gen.getUserCert("460010304607431869625000040505");
// KeyStore ks = KeyStore.getInstance("jks");
// FileInputStream ksfis = new FileInputStream("D:/linshi/" +
// "460010304607431869625000040505.jks");
// char[] storePwd = "SHELLNDK!@#0".toCharArray();
// char[] keyPwd = "SHELLNDK!@#0".toCharArray();
// // 從給定輸入流中加載此 KeyStore。
// ks.load(ksfis, storePwd);
//
// ksfis.close();
// 返回與給定別名關聯的密鑰(私鑰),並用給定密碼來恢復它。必須已經通過調用 setKeyEntry,或者以
// PrivateKeyEntry
// 或 SecretKeyEntry 爲參數的 setEntry 關聯密鑰與別名。
// PrivateKey privK = (PrivateKey) ks.getKey("USER#CERT%v1", keyPwd);
// System.out.println(BASE64.encode(privK.getEncoded()));
}
public Map<String, Object> getUserCert(String filename) {
Map<String, Object> map = new HashMap<String, Object>();
InputStream in = GenX509Cert.class.getClassLoader()
.getResourceAsStream("ca.properties");
Properties config = new Properties();
try {
config.load(in);
in.close();
} catch (IOException e) {
map.put("resultCode", "1011");
map.put("resultmsg", "解析ca配置文件失敗");
return map;
}
// 獲取apk信息
String caAlias = config.getProperty("caAlias");
String caPwd = config.getProperty("caPwd");
String caFilepath = config.getProperty("caFilepath");
String subFilepath = config.getProperty("subFilepath");
String subInfo = config.getProperty("subInfo");
String subPwd = config.getProperty("userPwd");
String subAlias = config.getProperty("subAlias");
try {
signCert(caAlias, caPwd, caFilepath, subInfo, subAlias, subPwd,
subFilepath, filename);
System.out.println("signCert完畢");
} catch (Exception e) {
e.printStackTrace();
map.put("resultCode", "1012");
map.put("resultmsg", "生成用戶證書失敗");
return map;
}
map.put("resultCode", "0");
map.put("userPublicKey", publickey);
map.put("userCert", userprivKey);
return map;
}
public String getServerCert() {
/*
* 創建ca證書,該操作只需只需一次,執行條件是更換服務器證書時,有效期10年。
*
* 運行環境:jdk1.6
*
* 參數說明: caInfo --指定證書擁有者信息,本例中可以不用修改,分別表示: ? OU(Organization Unit -
* 組織單位名稱) ? O(Organization - 組織名稱) ? L(Locality - 城市或區域名稱) ? ST(State -
* 州或省份名稱) ? C(Country - 國家名稱) caAlias --產生別名,用RootCA就可以,不用變。 caPwd
* --指定別名條目的密碼,本例中該密碼同時也是jks密碼 caFilepath
* --證書保存路徑,運行後將在該路徑中生成【RootCa.jks】【RootCa.crt】兩個文件, 文件說明:
* 其中【RootCa.jks】用於部署應用服務器的https服務時用 其中【RootCa.crt】文件用於預埋在客戶端中,驗證服務器證書用。
*/
Map<String, Object> map = new HashMap<String, Object>();
InputStream in = GenX509Cert.class.getClassLoader()
.getResourceAsStream("ca.properties");
Properties config = new Properties();
try {
config.load(in);
in.close();
} catch (IOException e) {
map.put("resultCode", "1011");
System.out.println("解析ca配置文件失敗");
return "1011";
}
String caAlias = config.getProperty("caAlias");
String caPwd = config.getProperty("caPwd");
String caFilepath = config.getProperty("caFilepath");
String caInfo = config.getProperty("subInfo");
// 生成rootCA證書
try {
createRootCA(caInfo, caAlias, caPwd, caFilepath);
} catch (Exception e) {
e.printStackTrace();
System.out.println("生成CA證書失敗!");
return "1012";
}
System.out.println("createRootCA完畢");
return serCert;
}
}
subInfo=CN\=userCert,OU\=user,O\=SDK,L\=SY,S\=LN,C\=CN
caAlias=RootCA
caPwd=Link@1234
#caFilepath=/home/weblogic/Oracle/Middleware/user_projects/domains/shu00/
#subFilepath=/home/weblogic/Oracle/Middleware/user_projects/domains/app/pfx/
#caFilepath=/home/weblogic/Oracle/Middleware/user_projects/domains/SDK/
#subFilepath=/home/weblogic/Oracle/Middleware/user_projects/domains/SDK/pfx/
caFilepath=F:/
subFilepath=F:/
subAlias=USER\#CERT%v1
userPwd=SHELLNDK\!@\#0
調用main函數會生產服務器的證書,調用getUserCert方法生產用戶證書。
在生成用戶證書時可能會出現錯誤:
java.security.InvalidKeyException: Illegal key size or default parameters
Java幾乎各種常用加密算法都能找到對應的實現。因爲美國的出口限制,Sun通過權限文件(local_policy.jar、US_export_policy.jar)做了相應限制。因此存在一些問題:
●密鑰長度上不能滿足需求(如:java.security.InvalidKeyException: Illegal key size or default parameters);
●部分算法未能支持,如MD4、SHA-224等算法;
●API使用起來還不是很方便;一些常用的進制轉換輔助工具未能提供,如Base64編碼轉換、十六進制編碼轉換等工具。
解決辦法:
Oracle在其官方網站上提供了無政策限制權限文件(Unlimited Strength Jurisdiction Policy Files),我們只需要將其部署在JRE環境中,就可以解決限制問題。
文件位於${java_home}/jre/lib/security
所以下載匹配的jce_policy
,替換jdk安裝目錄下 jdk1.* \jre\lib\security 中的 local_policy.jar 和 US_export_policy.jar 兩個jar包
Java
6 無政策限制文件:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
Java 7 無政策限制文件:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
其他版本
無政策限制文件:http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html
注意事項:
1.生成證書類依賴jdk1.6的rt.jar
2.配置tomcat server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="RootCa.jks的路徑" keystorePass="生成時的密碼" />