针对pfx文件的解析及操作,java 标准库提供了很好的支持,需要做一定得封装,才能比较友好的使用,如下是我做的一些封装及核心代码,话不多说,直接上代码
本次JDK版本 zulu-11
pfx自定义封装类:方便使用公私钥信息
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;
@Data
@NoArgsConstructor
public class PfxObj implements Serializable {
// 私钥
private PrivateKey privateKey;
// 公钥数组
private Certificate[] certificateChain;
// 核心公钥信息,包含公钥、过期日期等等
private X509Certificate x509Certificate;
// 证书公钥过期时间
private Date expiredAt;
private String commonName;
//alias
private String aliasName;
}
pfx解析及读写操作核心工具类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.bindo.module.PfxObj;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class PfxTools {
public final static String UPDATE_PFX_KEY="manual_update_pfx";
public final static String JUST_ONE_TASK="JUST_ONE_TASK";
public final static Map<String,Date> MANUAL_UPDATE_PFX = new ConcurrentHashMap<>();
/**
* 读取pfx文件,获取KeyStore对象
* @param keyStorePath pfx文件
* @param password pfx访问密码
* @return
*/
public static KeyStore loadKeyStore(String keyStorePath, String password) {
KeyStore keyStore = null;
FileInputStream fisFrom = null;
try {
keyStore = KeyStore.getInstance("PKCS12");
fisFrom = new FileInputStream(keyStorePath);
keyStore.load(fisFrom, password.toCharArray());
} catch (Exception e) {
log.error("加载pfx时异常,file" + keyStorePath, e);
} finally {
if (fisFrom != null) {
try {
fisFrom.close();
} catch (IOException e) {
log.error("关闭文件流异常.", e);
}
}
}
return keyStore;
}
/**
* 保存KeyStore对象至pfx文件
* @param ksFrom KeyStore对象
* @param fromPw pfx访问密码
* @param storePath pfx保存路径及文件
* @return
*/
public static boolean storeKeyStore(KeyStore ksFrom, String fromPw, String storePath) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(storePath);
ksFrom.store(fileOutputStream, fromPw.toCharArray());
} catch (Exception e) {
log.error("保存pfx到文件异常.storePath=" + storePath, e);
return false;
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
log.error("关闭文件流异常.", e);
}
}
}
return true;
}
/**
* 读取pfx文件,生成pfx自定义对象(PfxObj)集合
* @param pfxFileStr pfx文件
* @param pfxPassword pfx访问密码
* @return
* @throws Exception
*/
public static Map<String, PfxObj> readPfxFile(String pfxFileStr, String pfxPassword) throws Exception {
Map<String, PfxObj> pfxObjMap = new HashMap<>();
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream(pfxFileStr);
// If the keystore password is empty(""), then we have to set
// to null, otherwise it won't work!!!
char[] nPassword = null;
if ((pfxPassword == null) || pfxPassword.trim().equals("")) {
nPassword = null;
} else {
nPassword = pfxPassword.toCharArray();
}
ks.load(fis, nPassword);
fis.close();
// System.out.println("keystore type=" + ks.getType());
// Now we loop all the aliases, we need the alias to get keys.
// It seems that this value is the "Friendly name" field in the
// detals tab <-- Certificate window <-- view <-- Certificate
// Button <-- Content tab <-- Internet Options <-- Tools menu
// In MS IE 6.
Enumeration enumas = ks.aliases();
String keyAlias = null;
PrivateKey prikey = null;
int times = 0;
while (enumas.hasMoreElements())// we are readin just one certificate.
{
keyAlias = (String) enumas.nextElement();
prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
Certificate cert = ks.getCertificate(keyAlias);
X509Certificate x509Certificate = (X509Certificate) ks.getCertificate(keyAlias);
Certificate[] certificateChain = ks.getCertificateChain(keyAlias);
PublicKey pubkey = cert.getPublicKey();
times++;
PfxObj pfxObj = new PfxObj();
pfxObj.setCertificateChain(certificateChain);
pfxObj.setPrivateKey(prikey);
pfxObj.setX509Certificate(x509Certificate);
pfxObj.setAliasName(keyAlias);
pfxObj.setCommonName(((JSONObject) JSON.toJSON(x509Certificate)).getJSONObject("subjectDN").getString("commonName"));
pfxObj.setExpiredAt(x509Certificate.getNotAfter());
pfxObjMap.put(keyAlias, pfxObj);
}
// System.out.println("=============== 证书数量=" + times + " =============");
return pfxObjMap;
}
/**
* 以account为维度,获取pfx证书,多个证书时,取最新的过期时间
*
* @param pfxFileStr
* @param pfxPassword
* @return
* @throws Exception
*/
public static Map<String, PfxObj> getPfxToSpAccountMap(String pfxFileStr, String pfxPassword) throws Exception {
Map<String, PfxObj> pfxObjMap = readPfxFile(pfxFileStr, pfxPassword);
Map<String, PfxObj> spAccountPfxMap = new HashMap<>();
if (CollectionUtils.isEmpty(pfxObjMap)) {
return spAccountPfxMap;
}
Iterator<Map.Entry<String, PfxObj>> iterator = pfxObjMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, PfxObj> pfxCer = iterator.next();
if (spAccountPfxMap.containsKey(pfxCer.getValue().getCommonName())) {
if (spAccountPfxMap.get(pfxCer.getValue().getCommonName()).getExpiredAt().before(pfxCer.getValue().getExpiredAt())) {
spAccountPfxMap.replace(pfxCer.getValue().getCommonName(), pfxCer.getValue());
}
} else {
spAccountPfxMap.put(pfxCer.getValue().getCommonName(), pfxCer.getValue());
}
}
return spAccountPfxMap;
}
/**
* pfx的A文件写入至B文件
* 此处未保存到文件,只是存入B的KeyStore对象
* @param toFile 接收pfx的文件
* @param toPw 接收文件的访问密码
* @param fromFile 被复制的
* @param fromPw
* @return
* @throws Exception
*/
public static KeyStore writePFX(String toFile, String toPw, String fromFile, String fromPw) throws Exception {
KeyStore ksTo = KeyStore.getInstance("PKCS12");
FileInputStream fisTo = new FileInputStream(toFile);
ksTo.load(fisTo, toPw.toCharArray());
KeyStore ksFrom = KeyStore.getInstance("PKCS12");
FileInputStream fisFrom = new FileInputStream(fromFile);
ksFrom.load(fisFrom, fromPw.toCharArray());
Enumeration<String> aliases = ksFrom.aliases();
while (aliases.hasMoreElements()) {
String aliase = aliases.nextElement();
if (!ksFrom.isKeyEntry(aliase)) {
continue;
}
Key keyFrom = ksFrom.getKey(aliase, fromPw.toCharArray());
Certificate[] certificateChainFrom = ksFrom.getCertificateChain(aliase);
ksTo.setKeyEntry(aliase, keyFrom, toPw.toCharArray(), certificateChainFrom);
}
return ksTo;
}
/**
* pfx的A文件写入至B文件
* 只是写入到某个已存在的KeyStore对象,还未保存至文件
* @param ksTo KeyStore接收公私钥
* @param toPw 接收者的访问密码
* @param fromFile 待读取的pfx文件
* @param fromPw 访问密码
* @return
* @throws Exception
*/
public static KeyStore writePFX(KeyStore ksTo, String toPw, String fromFile, String fromPw) throws Exception {
KeyStore ksFrom = KeyStore.getInstance("PKCS12");
FileInputStream fisFrom = new FileInputStream(fromFile);
ksFrom.load(fisFrom, fromPw.toCharArray());
Enumeration<String> aliases = ksFrom.aliases();
while (aliases.hasMoreElements()) {
String aliase = aliases.nextElement();
if (!ksFrom.isKeyEntry(aliase)) {
continue;
}
Key keyFrom = ksFrom.getKey(aliase, fromPw.toCharArray());
Certificate[] certificateChainFrom = ksFrom.getCertificateChain(aliase);
ksTo.setKeyEntry(aliase, keyFrom, toPw.toCharArray(), certificateChainFrom);
}
return ksTo;
}
/**
* pfx的A文件写入至B文件
* 将pfx自定义对象写入到KeyStore,未保存至文件
* @param ksTo KeyStore接收公私钥
* @param toPw 接收者的访问密码
* @param pfxObj pfx自定义对象
* @return
* @throws Exception
*/
public static KeyStore writePFX(KeyStore ksTo, String toPw, PfxObj pfxObj) throws Exception {
ksTo.setKeyEntry(pfxObj.getCommonName(), pfxObj.getPrivateKey(), toPw.toCharArray(), pfxObj.getCertificateChain());
return ksTo;
}
/**
* 从pfx文件中删除某个alias的公私钥信息
* 未保存至文件
* @param fromFile
* @param fromPw
* @param aliaseRemove
* @return
* @throws Exception
*/
public static KeyStore removePFX(String fromFile, String fromPw, String aliaseRemove) throws Exception {
KeyStore ksFrom = KeyStore.getInstance("PKCS12");
FileInputStream fisFrom = new FileInputStream(fromFile);
ksFrom.load(fisFrom, fromPw.toCharArray());
Enumeration<String> aliases = ksFrom.aliases();
boolean removeFlag = false;
while (aliases.hasMoreElements()) {
String aliase = aliases.nextElement();
if (aliase.equalsIgnoreCase(aliaseRemove)) {
removeFlag = true;
}
}
if (removeFlag) {
ksFrom.deleteEntry(aliaseRemove);
}
return ksFrom;
}
/**
* 从KeyStore删除某个公私钥对
* @param ksFrom
* @param fromPw
* @param aliaseRemove
* @return
* @throws Exception
*/
public static KeyStore removePFX(KeyStore ksFrom, String fromPw, String aliaseRemove) throws Exception {
Enumeration<String> aliases = ksFrom.aliases();
boolean removeFlag = false;
while (aliases.hasMoreElements()) {
String aliase = aliases.nextElement();
if (aliase.equalsIgnoreCase(aliaseRemove)) {
removeFlag = true;
}
}
if (removeFlag) {
ksFrom.deleteEntry(aliaseRemove);
}
return ksFrom;
}
/**
* 下载pfx文件
* @param fileUri
* @param downloadedFile
* @throws Exception
*/
public static void downloadNetFile(String fileUri, String downloadedFile) throws Exception {
// 下载网络文件
int bytesum = 0;
int byteread = 0;
URL url = new URL(fileUri);
InputStream inStream = null;
FileOutputStream fs = null;
try {
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
fs = new FileOutputStream(downloadedFile);
byte[] buffer = new byte[1204];
int length;
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread;
fs.write(buffer, 0, byteread);
}
fs.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
inStream.close();
fs.close();
}
}
/**
* 执行shell命令
* @param commands
* @return
*/
public static List<String> executeNewFlow(List<String> commands) {
List<String> rspList = new ArrayList<String>();
Runtime run = Runtime.getRuntime();
try {
Process proc = run.exec("/bin/bash", null, null);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
for (String line : commands) {
out.println(line);
}
out.println("exit");// 这个命令必须执行,否则in流不结束。
String rspLine = "";
while ((rspLine = in.readLine()) != null) {
System.out.println(rspLine);
rspList.add(rspLine);
}
proc.waitFor();
in.close();
out.close();
proc.destroy();
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return rspList;
}
/**
* 是否在有效期内
*
* @param expriedDate 过期时间
* @param days 即将过期预警天数
* @return
*/
public static boolean isValidIn(Date expriedDate, int days) {
int expiredInTimes = days * 24 * 60 * 60 * 1000;
long gapTime = expriedDate.getTime() - new Date().getTime();
return gapTime - expiredInTimes > 0 ? true : false;
}
public static Integer extractSpId(String str) {
if (StringUtils.isBlank(str) || !str.startsWith("SP") || !str.contains("-")) {
log.error("无效的sp身份ID=" + str);
return null;
}
int index = str.indexOf("-");
Integer spId = null;
try {
spId = Integer.valueOf(str.substring(2, index));
} catch (NumberFormatException e) {
log.error("无效的sp身份ID=" + str, e);
}
return spId;
}
public static void main(String[] args) throws Exception {
// Map<String, PfxObj> pfxObjMap = readPfxFile("/Users/bindo/Downloads/ecert.pfx", "123456");
// Iterator<String> iterator = pfxObjMap.keySet().iterator();
// Map<String,Integer> commonNameMap = new HashMap<>();
// while (iterator.hasNext()){
// String key = iterator.next();
// String commonName = pfxObjMap.get(key).getCommonName();
// if (commonNameMap.containsKey(commonName)){
// commonNameMap.put(commonName,commonNameMap.get(commonName)+1);
// }else {
// commonNameMap.put(commonName,1);
// }
// // System.out.println("证书的alias="+key+", commonName="+commonName+", 有效期="+pfxObjMap.get(key).getX509Certificate().getNotAfter()+", 是否失效="+pfxObjMap.get(key).getX509Certificate().getNotAfter().before(new Date()));
//
// }
// Iterator<String> iterator1 = commonNameMap.keySet().iterator();
// // System.out.println("sip是否有同名:");
// while (iterator1.hasNext()){
// String next = iterator1.next();
// // System.out.println(next+"="+commonNameMap.get(next));
// }
// downloadNetFile("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2020-07-23%2F5f195601471b5.jpg&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659774124&t=330917ba468613cc969d159e4a7d1a28","/Users/bindo/Downloads/OSDX-3/abc.gif");
// System.out.println(extractSpId("SP59938s5t-_mop"));
}
}