# 1.RSA格式
密钥格式:PKCS#1 ,PKCS#8
PKCS#1形式的密钥专指RSA的密钥,如果一个ECC的密钥就无法用PKCS#1形式来表达。那么有没有一个通过的机构既可以表示RSA密钥,又可以表示ECC的密钥呢?有,这个就是PKCS#8形式的密钥。
PKCS#1和PKCS#8私钥生成方式不一样
获取PKCS#1
```
public static PrivateKey getPKCS1Key(byte[] privateKeyBytes ) throws Exception{
// 取得私钥 for PKCS#1
RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(privateKeyBytes));
RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
PrivateKey priKey= keyFactory.generatePrivate(rsaPrivKeySpec);
return priKey;
}
```
PKCS#8
证书有三种类型:X.509证书、PKCS#7证书、PKCS#12证书
X.509只包含公钥,没有私钥,这种证书一般公开发布,可用于放在客服端使用,用于加密、验签。
PKCS#12就定义了这样一种证书,它既包含了公钥有包含了私钥。典型的入pfx、p12证书就是PKCS#12证书。
PKCS#7就定义了这样一个证书链的类型结构。典型如p7b后缀名的证书就是这样的格式。 (java不支持PKCS#7)
X509证书使用
```
@Test
public void testVerifyZhangsanCert() throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
FileInputStream inStream = new FileInputStream("H:/certtest/zhangsan.cer");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(inStream);
System.out.println(certificate.getPublicKey().getClass());
Signature signature = Signature.getInstance(certificate.getSigAlgName());
signature.initVerify(getRootPublicKey());
signature.update(certificate.getTBSCertificate());
boolean legal = signature.verify(certificate.getSignature());
System.out.println(legal);
}
```
PKCS#12使用要导入
```
public class P12Application {
public static void main(String[] args) throws Exception {
//从文件中解析
try (FileInputStream is = new FileInputStream("apiclient.p12")) {
System.out.println(P12Info.parse(is, "证书密码"));
}
//从Base64加密串中解析
String p12Base64 = "xxxyyyzzz";
byte[] base64byte = Base64.getDecoder().decode(p12Base64);
try (ByteArrayInputStream is = new ByteArrayInputStream(base64byte)) {
System.out.println(P12Info.parse(is, "证书密码"));
}
}
@Data
@Builder
private static class P12Info {
/**
* 证书序列号.
*/
private final String serialNo;
/**
* 证书秘钥.
*/
private final PrivateKey privateKey;
/**
* 公钥.
*/
private final PublicKey publicKey;
public static P12Info parse(InputStream is, String passwd) throws Exception {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(is, passwd.toCharArray());
String keyAlias = null;
//解析证书,必须有别名
Enumeration<String> aliases = ks.aliases();
if (aliases.hasMoreElements()) {
keyAlias = aliases.nextElement();
}
//解析私钥
PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, passwd.toCharArray());
Certificate cert = ks.getCertificate(keyAlias);
BigInteger serialNumber = ((X509CertImpl) cert).getSerialNumber();
//证书一般都使用16进制表示
String certSn = serialNumber.toString(16);
//设置证书序列号和私钥
return P12Info.builder()
.serialNo(certSn)
.privateKey(privateKey)
.publicKey(cert.getPublicKey())
.build();
}
}
}
```
两种证书解析方式不一样
**RSA加密的填充方式**
[RSA](https://so.csdn.net/so/search?q=RSA&spm=1001.2101.3001.7020)加密常用的填充模式有三种:`RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING, RSA_NO_PADDING。`
java本身只支持NoPadding和PKCS5Padding
1.RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2.RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41
输出:和modulus一样长
3.for RSA_NO_PADDING 不填充
输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充
```
在实际做任何形式加密之前,需要有公钥和私钥两个密钥对。幸运的是,Java提供了非常简单的方法,请看示例代码:
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
return pair;
}
```
# 常见签名算法SHA1WithRSA、MD5withRSA生成签名以及验签
```
//用私钥给入参加签
Signature sign = Signature.getInstance("SHA1WithRSA");
sign.initSign(privatekey);
sign.update(param.getBytes());
signature = sign.sign();
```
*