开发中交互过程中经常需要对传输数据的保密性和完整性有要求,因此需要对传输的数据加密和签名防止窃取和篡改,首选对加密文件的定义:
通常情况下,作为文件形式存在的证书一般有三种格式:
第一种:带有私钥的证书,由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以.pfx作为证书文件后缀名。
第二种:DER Encoded Binary (.cer) 二进制编码的证书,证书中没有私钥,DER 编码二进制格式的证书文件,以.cer作为证书文件后缀名。
第三种:Base64 Encoded(.cer),Base64编码的证书,证书中没有私钥,BASE64 编码格式的证书文件,也是以.cer作为证书文件后缀名。
定义可以看出,只有pfx格式的数字证书是包含有私钥和公钥的,cer格式的数字证书里面只有公钥没有私钥。
这里的公钥要私钥只是代表文件内容,并不是对公钥和私钥的定义,可认为能公开让大家都知道的文件为公钥,可以是后缀PFX的这个文件也可以是CER后缀的这个文件,这两个文件的本质是RSA加密的方式一种实现,其实就是两个互质的数,DER加密的文件PFX能解密,PFX加密的文件DER也能解密。
操作java加密和解密需要对KeyStore 和 Cipher 这两个类有大致的了解和使用。
首先是对 数据的加密PFX的使用示例,PFX文件的密码默认123456:
/**
* 根据私钥文件加密
*
* @param src
* @param pfxPath
* @param priKeyPass
* @return
*/
public static String encryptByPriPfxFile(String src, String pfxPath, String priKeyPass) {
PrivateKey privateKey = getPrivateKeyFromFile(pfxPath, priKeyPass);
if (privateKey == null) {
return null;
}
return encryptByPrivateKey(src, privateKey);
/**
* 根据私钥加密
*
* @param src
* @param privateKey
*/
public static String encryptByPrivateKey(String src, PrivateKey privateKey) {
byte[] destBytes = rsaByPrivateKey(src.getBytes(), privateKey, Cipher.ENCRYPT_MODE);
if (destBytes == null) {
return null;
}
return FormatUtil.byte2Hex(destBytes);
}
/**
* 根据私钥路径读取私钥
*
* @param pfxPath
* @param priKeyPass
* @return
*/
public static PrivateKey getPrivateKeyFromFile(String pfxPath, String priKeyPass) {
InputStream priKeyStream = null;
try {
priKeyStream = new FileInputStream(pfxPath);
byte[] reads = new byte[priKeyStream.available()];
priKeyStream.read(reads);
return getPrivateKeyByStream(reads, priKeyPass);
} catch (Exception e) {
// log.error("解析文件,读取私钥失败:", e);
} finally {
if (priKeyStream != null) {
try {
priKeyStream.close();
} catch (Exception e) {
//
}
}
}
return null;
}
/**
* 根据PFX私钥字节流读取私钥
*
* @param pfxBytes
* @param priKeyPass
* @return
*/
public static PrivateKey getPrivateKeyByStream(byte[] pfxBytes, String priKeyPass) {
try {
String KEY_PKCS12 = "PKCS12";
KeyStore ks = KeyStore.getInstance(RsaConst.KEY_PKCS12);
char[] charPriKeyPass = priKeyPass.toCharArray();
ks.load(new ByteArrayInputStream(pfxBytes), charPriKeyPass);
Enumeration<String> aliasEnum = ks.aliases();
String keyAlias = null;
if (aliasEnum.hasMoreElements()) {
keyAlias = (String) aliasEnum.nextElement();
}
return (PrivateKey) ks.getKey(keyAlias, charPriKeyPass);
} catch (IOException e) {
// 加密失败
// log.error("解析文件,读取私钥失败:", e);
} catch (KeyStoreException e) {
// log.error("私钥存储异常:", e);
} catch (NoSuchAlgorithmException e) {
// log.error("不存在的解密算法:", e);
} catch (CertificateException e) {
// log.error("证书异常:", e);
} catch (UnrecoverableKeyException e) {
// log.error("不可恢复的秘钥异常", e);
}
return null;
}
/**
* 私钥算法
*
* @param srcData
* 源字节
* @param privateKey
* 私钥
* @param mode
* 加密 OR 解密
* @return
*/
public static byte[] rsaByPrivateKey(byte[] srcData, PrivateKey privateKey, int mode) {
try {
String RSA_CHIPER = "RSA/ECB/PKCS1Padding";
Cipher cipher = Cipher.getInstance(RsaConst.RSA_CHIPER);
cipher.init(mode, privateKey);
// 分段加密
int blockSize = (mode == Cipher.ENCRYPT_MODE) ? cipher.getOutputSize(srcData.length)-11 : cipher.getOutputSize(srcData.length);
byte[] decryptData = null;
for (int i = 0; i < srcData.length; i += blockSize) {
byte[] doFinal = cipher.doFinal(subarray(srcData, i, i + blockSize));
decryptData = addAll(decryptData, doFinal);
}
return decryptData;
} catch (NoSuchAlgorithmException e) {
// //log.error("私钥算法-不存在的解密算法:", e);
} catch (NoSuchPaddingException e) {
//log.error("私钥算法-无效的补位算法:", e);
} catch (IllegalBlockSizeException e) {
//log.error("私钥算法-无效的块大小:", e);
} catch (BadPaddingException e) {
//log.error("私钥算法-补位算法异常:", e);
} catch (InvalidKeyException e) {
//log.error("私钥算法-无效的私钥:", e);
}
return null;
}
public static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) {
if (array == null) {
return null;
}
if (startIndexInclusive < 0) {
startIndexInclusive = 0;
}
if (endIndexExclusive > array.length) {
endIndexExclusive = array.length;
}
int newSize = endIndexExclusive - startIndexInclusive;
if (newSize <= 0) {
return new byte[0];
}
byte[] subarray = new byte[newSize];
System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
return subarray;
}
public static byte[] addAll(byte[] array1, byte[] array2) {
if (array1 == null) {
return clone(array2);
} else if (array2 == null) {
return clone(array1);
}
byte[] joinedArray = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, joinedArray, 0, array1.length);
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
return joinedArray;
}
/**
* 将byte[] 转换成字符串
*/
public static String byte2Hex(byte[] srcBytes) {
StringBuilder hexRetSB = new StringBuilder();
for (byte b : srcBytes) {
String hexString = Integer.toHexString(0x00ff & b);
hexRetSB.append(hexString.length() == 1 ? 0 : "").append(hexString);
}
return hexRetSB.toString();
}
大致流程为先用文件流InputStream 读取到PFX文件到byte[]数组中,在通过KeyStore类加载出来PrivateKey,拿到私钥之后就可以使用Cipher对需要加密的数据进行操作了,操作完成之后就是byte数据在转成16进制的字符串就是加密完成的数据了。
CER的待更新。。。