建立根证书流程如下:
//生成私钥,产生CARoot.key文件,需要输入私钥保护口令
openssl genrsa -des3 -out CARoot.key 2048
//生成证书请求,需要输入CARoot.key私钥保护口令。根据提示输入:国家、省份、组织名、邮箱、等信息。最后生成CARoot.csr证书请求文件。
openssl req -new -key CARoot.key -out CARoot.csr -config openssl_root_ca.cnf
//签发证书,最后生成自签名的根证书 CARoot.crt
openssl req -x509 -days 666 -key CARoot.key -in CARoot.csr -out CARoot.crt -config openssl_root_ca.cnf
下面将介绍以CARoot.crt为根证书签发下级证书的过程。
//生成自己的私钥
openssl genrsa -des3 -out workCert.key 2048
//生成证书请求文件
openssl req -new -key workCert.key -out workCert.csr -config openssl_root_ca.cnf
//根证书签发下级证书,一定要带上-CAcreateserial参数,否则可能报下面的错误(CAroot.srl No such file or directory)
openssl x509 -req -CA CAroot.crt -CAkey CAroot.key -in workCert.csr -out workCert.crt -days 3650 -CAcreateserial -passin pass:123456
CAroot.crt: CA的证书
CAroot.key: CA的私钥
123456: CA的密码
workCert.csr: 证书请求文件
workCert.crt: 被CA签名后的服务器证书
openssl_root_ca.cnf的配置如下
[ ca ]
default_ca = CA_default
[ CA_default ]
# 放置相关的文件和目录.
dir = D:/test/ca/root
certs = $dir/cert
new_certs_dir = $dir/signed_certs
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# 放置私钥和证书的路径.
private_key = $dir/private/root_ca.key.pem
certificate = $dir/cert/root_ca.cert.pem
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 365
preserve = no
policy = policy_defualt
[ policy_defualt ]
# 签发证书文件资料的检查 (和根证书必须一样).
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# req 工具需要的參数.
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
[ req_distinguished_name ]
# 生成证书是要输入的一些说明信息
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ root_ca ]
# 签发根证书时使用
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ intermediate_ca ]
# 签发和发布时使用
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
备注:
dir = D:/test/ca/root为你自己电脑的目录
在该 目录下需创建cert、private、signed_certs目录,创建index.txt,serial文件,openssl_root_ca.cnf也在该目录下
说明:
CARoot.crt证书打开,内容是 以下这种格式
-----BEGIN CERTIFICATE-----
MIIDfzCCAmcCFDshuNrLclYAPomoTG7AHKMwdO94MA0GCSqGSIb3DQEBCwUAMHwx
CzAJBgNVBAYTAnpnMQswCQYDVQQIDAJoYjEOMAwGA1UEBwwFd3VoYW4xDTALBgNV
BAoMBHdodHkxDTALBgNVBAsMBHdodHkxDTALBgNVBAMMBHdodHkxIzAhBgkqhkiG
9w0BCQEWFHlhbmdtaW5nQHdodHkuY29tLmNuMB4XDTIyMDcyMjA2MDgxOVoXDTI0
MDUxODA2MDgxOVowfDELMAkGA1UEBhMCemcxCzAJBgNVBAgMAmhiMQ4wDAYDVQQH
DAV3dWhhbjENMAsGA1UECgwEd2h0eTENMAsGA1UECwwEd2h0eTENMAsGA1UEAwwE
WbmVxkmFJxEGBRtJV2XdLzkFDP//XCY0PY3TdSVK7zKGyIwwhu5BvZKOCqTMU8As
Annpra/Oivpg7lyBlmIPT6rLDnogfZMeIOT2sRCXDFAC1j5TAcASxtui+9Dg/ohm
rJCXpQkpfUf7g5dfQr/VdWoYckyOK0OqhIIcD7s0CAM1Qu15Sjhd0pLrAgMBAAEw
DQYJKoZIhvcNAQELBQADggEBAEHpkhO4V3XJtW0tPRqPOBZ+q+cQv4OvoXQ8/AT3
dpC9vvV3x9ke7hFPQ+52Y9VyynirmfUQ23ZrmOFmeQLj97g=
-----END CERTIFICATE-----
可以使用以下代码进行解析,然后使用公钥加密
//这里直接把crt证书文件的base64部分取出,作为base64的字符串使用
String hexStr = Base64Util.base64Decode(rootCertBase64);
byte[] decode = Forms.hexStringToByte(hexStr);
//将内容转成流的方式
ByteArrayInputStream bis = new ByteArrayInputStream(decode);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate certificate = cf.generateCertificate(bis);
String data = "aaaabbbbccccdddd";
System.out.println("根证书公钥加密原数据: " + data);
//取出公钥--这里的公钥是pkcs8的那种结构型--待核实
PublicKey rootCertPublicKey = certificate.getPublicKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rootCertPublicKey);
byte[] encData = cipher.doFinal(data.getBytes());
System.out.println("根证书公钥加密: " + Forms.byteToHexString(encData));
CARoot.key打开,内容是如下格式,该格式是PKCS#1编码格式,不能直接使用。
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,2F477D49DBCF844A
/Snd+opdw9ZNeRfSAiVZJQalI3w3H5T19ojHaf0X6HL1vt5GUMdVgqLv/guXLjPw
/AUqucLbhX/372X5YKgBVneF2wkP2ppHj0etrpJm1pQlKcJjTqFo6DNMGYEUepwc
lNaYYhRBDprffHKiSXM2OFGvzbmJ0s4zabwAgT5hE7BhrDKknOjadDFd9bFI0lX8
LJF7XcUbAbQHPnTcCXp1D4iksJgGA0mjmuB7DgG19TjCTTUdk+L139R8r7UiHsG9
jNJ1X7gvrfkNKeoloTLEdiuYSCxhOd50dAqKpPxlW0RePxUw6iEwNfqmrcOzHUOu
IgcK149cDiqlj27QF5FA1RlJcajsgXFK0yICi9Za1Kz3cKdUmyYxkcBwBG6VcsV1
XQewbOBz8qqMyjyX1u+SQGlxDALCls29Hh3e+Jr5qf1TmZ8nt9CdXzITgeoHmohN
fa2KEyb0Mk1pNMgzYPWXhKsmnFcip2S0Bd8qRhgLVK4Jq5SoWZ2TguD0G+xmNRp1
VHthal0BctDg2oRf/mkoepMEJNGkOH8zVuhfT9D90kuuGtnclYXtCXgyn4QtMk3f
xJGuCE9jc9rs3lKFDgXsOxJxa6ncphCfwQLE1oDrEa+nhEo5RJVSewvuVpyJBNQE
HCuNXRDdxc4NE6SxFKm5CHdJTY5O4YB743b7FX9exzhHd3qUNuh8/Kd6Eht/8RQ2
U4XZ9cm4Z0P4Olrm+sPKaVo4r71/jnXrsjGQlZGKCVbZq2R4r0/jtMCFTogK7wu7
01PGURWPxCdTTPrU8PwIKF12SLNQFuGi8ngZzzC0asmJsyFEO+LJoMsa+ZY22xGa
dxxrJQciX2uRrn99nboHLcLorhcm8oA+9XvvXNLB13+jnRUIpip6F5PgJq7OnTxg
-----END RSA PRIVATE KEY-----
需要先将PKCS#1的编码格式转变为PKCS#8编码的格式,内容如下:
转变的命令为:openssl pkcs8 -topk8 -in CARoot.key -out CARoot.key.p8 -nocrypt
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCe68zN+uVKx0rB
UoYxD/k4ypF7RR1PWfBDv+mf83JGX6yNeXjfb6/G+vaDoHQkur5+jLCbyZ8NZQRp
xyReXfSbigFcRA40nsfN2XxS2r/D6bI6hIqS9At5T7mj/vTmKd+esSXkamGLBAJ6
wlA1ABzDilOcrDyiNmIeCnUQ5E3xxpkMm8w2UpS+KzAodRrtFhQ6gK8UVVwDMF4k
prEDhTOfKy0rM5W3i4o1p11uaW7G6dPeBtPdUcEW5ZIUkzt+y7YCPsrYa8FZVVMJ
yIyaRkp2a+D7st4hgPBUcbpvYItd5XUKKiLXKmiJ9We9w+TA4/J/7iygDu1L4pyH
sgcGDIBBAgMBAAECggEBAJcyAezqSeHGD4Iz0EW3S8XTO2wLCmKnT6Y1XMD3rX30
okcLDYuOR67Ir2X1bupO3NOraI8e2Hq24+b7NsetHf4o4zhz6wv3EGLxCo4NXiq3
GQEBfpFnK89joVu8QiUEK6NgKIvTBjBrRSyj2AlErRkAbqH9X8Z4dQrdqhjDDMD7
UGpdeAa/xI5LjfYlDl44HSGrTqbkC+grNNgeqs+3FrD9FoVVaZz6XLSVuHMD+Thm
MZJ4Dnbf2s3eYKLj8chMiodOYvr1y5H67wSfmqFuB70iU4ZIhgJnzAOM1dmQZArV
2C1mLSRV9FjUJTchLA36Wr/3Z7Ij6QXRNKzk/U/nTscwFOiQA/cV/uMfJT/LKssq
czlrRA92IZ2YIIApPE3mttW6
-----END PRIVATE KEY-----
以上PKCS#8的格式使用如下:
String base64Decode = Base64Util.base64Decode(privateKeyBase64);
// System.out.println("下级证书私钥base64Decode: " + base64Decode);
byte[] keyBytes = Forms.hexStringToByte(base64Decode);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decData = cipher.doFinal(encData);
System.out.println("下级证书私钥解密: " + Forms.byteToHexString(decData));
公钥加密,私钥解密;私钥签名,公钥验签
public static void testPubKey(){
try {
//公钥证书
String workCert = "MIIDtzCCAp8CFEHdcA7b5rGZmtBflLQ6O/97RZW2MA0GCSqGSIb3DQEBCwUAMIGN\n" +
"MQswCQYDVQQGEwJ6ZzEOMAwGA1UECAwFaHViZWkxEzARBgNVBAcMCnlhbmdfaHVi\n" +
"ZXUxEzARBgNVBAoMCnlhbmdfaHViZWkxEzARBgNVBAsMCnlhbmdfaHViZWkxEzAR\n" +
"BgNVBAMMCnlhbmdfaHViZWkxGjAYBgkqhkiG9w0BCQEWC3lhbmdAcXEuY29tMB4X\n" +
"DTIyMDczMDAzMDQxNVoXDTMyMDcyNzAzMDQxNVowgaExCzAJBgNVBAYTAnpnMQ4w\n" +
"DAYDVQQIDAVodWJlaTEYMBYGA1UEBwwPeWFuZ193b3JrX2h1YmVpMRgwFgYDVQQK\n" +
"DA95YW5nX3dvcmtfaHViZWkxGDAWBgNVBAsMD3lhbmdfd29ya19odWJlaTEYMBYG\n" +
"A1UEAwwPeWFuZ193b3JrX2h1YmVpMRowGAYJKoZIhvcNAQkBFgt5YW5nQHFxLmNv\n" +
"bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM14s8Y+Ky9EWRhkEsxq\n" +
"qPNaGS+FyUdLJMR7v9SUY0fH0UpYMV7hYRZC1SEeK8Ig5GwIhPRIQ2dotr8oDd6R\n" +
"kNytraMd668998o9TavHVjNQPzKuPNDtBklD0TNB2a5p/4aw5sC8kUh1WBtAAyz9\n" +
"yZ8gZu5EAxSZd2lm/t1AAv06JNTDVqQqH2C0UUeRyQ6f3rKLOqQWMjoVysWqn+ge\n" +
"IfGlJO70R+3nF6q60epFY9CO/3kJ00xUi6CpOw0u9886bEPmhsr0dAcjPRCOkSWj\n" +
"xMCLlb7aTKEQAK1wPiSiDAgUzwlOEFlhF4GX/dv2o8qcDqbxlGYQBSyBvm9UxrPO\n" +
"1m8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAD6zKXeudVHVtY/tuPjipOSuLTmhU\n" +
"+pbymWelXYVFuvLgm24p+L3w4/NK/51hIMMhzX4DrXbVExAqF5oHA3OdCwn/VoRR\n" +
"V/tdbZhWDUUS/bE9oj1UyQcP9cLHm5YQ6fCKgvqy6yfgp5JaV5u72gVwJrHcZcuh\n" +
"wJshkb+V1OEdgAz05P47yxlTP029dVvI9159SPewMss6D9JvutQFXsAeNIGIKmJy\n" +
"0auCbKKNmngP4UnkIW6lUx9FhJ4tRk3nppIf48qkweIPbtWnNbyW+MeDTmIR3Bst\n" +
"T8/gZoX6ZLdrrLCJd7+BY7/vxHkx91Dn/AtsjskzzH+yOTN3UHqDMam9vw==";
//证书私钥
String workCertPrivateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNeLPGPisvRFkY\n" +
"ZBLMaqjzWhkvhclHSyTEe7/UlGNHx9FKWDFe4WEWQtUhHivCIORsCIT0SENnaLa/\n" +
"KA3ekZDcra2jHeuvPffKPU2rx1YzUD8yrjzQ7QZJQ9EzQdmuaf+GsObAvJFIdVgb\n" +
"QAMs/cmfIGbuRAMUmXdpZv7dQAL9OiTUw1akKh9gtFFHkckOn96yizqkFjI6FcrF\n" +
"qp/oHiHxpSTu9Eft5xequtHqRWPQjv95CdNMVIugqTsNLvfPOmxD5obK9HQHIz0Q\n" +
"jpElo8TAi5W+2kyhEACtcD4kogwIFM8JThBZYReBl/3b9qPKnA6m8ZRmEAUsgb5v\n" +
"VMazztZvAgMBAAECggEASyHDus2oxNPdMEoHha/Kr5FWfEYTX3peq2oErxrDYs/x\n" +
"+5lcFDGdaqJthHqfzZBxdf93fkueOgXULPnceHIJ4mevgmHCeIXUQaNA73soTgMd\n" +
"2M5Vdp/1GH0v1epSSrB/uyScfAV3qmMeIHi7sIvsWb9jY+SCq9miDGyY7RYnAajF\n" +
"J+7pgr+8bwLrV8KV8vXCNZD6zwH6NCUYcpsDCul2uE7n99gEKxJu27DqkaCCjiWE\n" +
"6dkeYfAXhhq3dj2SRrQZ+3ufL3E4QVxrpxAMAx8r7MwxuVka+ADeYZriFdREDYrQ\n" +
"2eb46H+IYk/iokSC1enfmkMdAegbc43dQrDaoMJQAQKBgQDyse8RoZTYC/d/3dao\n" +
"Y6HuBvEVVLbncc4DOHVwZ3ZOtTWASmbIUkTX54KzdCCYEy5n8yMtjDtNX7Y6N0hz\n" +
"Rzpj9PgTTTulIeqiFgjGRo1FWvDyte2LFSZZ4I59uRDDChOwqufVUotiVUJL/FgL\n" +
"yljRy5Kb7i7pEW17aSDtL20qAQKBgQDYvFwuQnPJVNR3OPReWsoYX9liHbXv6gCg\n" +
"XuS0t25Vt4asDG5u3gJ1SBJSI3g0UPf0TF0YPHYfzlJGsKvB48Kp66aXm8zPNa0J\n" +
"L8+kH567xpoC/d68qdNy5hRZId/jvmPt54Y0tUy/BjdNmqlDwLbkyZDjTj9m7lNz\n" +
"sEqGjzmgbwKBgA/sdM8nVujGADs3hvoNb3Z6ph7MLCQLZ4T2k14Iq86GPThpqUzD\n" +
"eRjha8XyfKD9wTG41joK1WlCOmafcfV+WupsDErX6m3vR8HSyEiaIHLIgL6sCSXz\n" +
"AU0sWq0NE3h4lMomrIdmnxaYmXz61ZwQbrt1K+1nN1S7e/946lwlnHgBAoGAY2Cq\n" +
"28M4mB4/dZM16XWzqM16PZTl8WXYd7BLKdnZy4/lPkpM8KT3d5NeYy9EVKizqN7C\n" +
"6PQALcFK1IL5nmOyxHr63hVgKbqw5r93dAfTnsIHqEuDr/omrE53Eg+IO0L3SwSX\n" +
"8t8Wm5hcD0dVSW257tnFh5Q/WhD5TtiMs3pEsB0CgYEA4cFqWFWXrxYmKo8oAIdt\n" +
"KknEQtO92IyRejZNrBwQjAK86ixxdUqybvNSLZdava3wpciwcg48yKlgOFBSOsfz\n" +
"vEFLJDozJj/Yeqoy/hhjqw6pHxf0n2YjlrBq+YWbJF2+U2FG6+01NA8i4v0ASWjz\n" +
"Vx5ffzP6TqAxECuyy+hDxDs=";
String mainKeyHex="74657374616263646566313030303031";
//公钥加密
String hexStr = Base64Util.base64Decode(workCert);
byte[] decode = Forms.hexStringToByte(hexStr);
// //将内容转成流的方式
ByteArrayInputStream bis = new ByteArrayInputStream(decode);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate certificate = cf.generateCertificate(bis);
//取出公钥--这里的公钥是pkcs8的那种结构型--待核实
PublicKey publicKey = certificate.getPublicKey();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//根据转换的名称获取密码对象Cipher(转换的名称:算法/工作模式/填充模式)
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
//用公钥初始化此Cipher对象(加密模式)
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//对数据加密
byte[] encrypt = cipher.doFinal(mainKeyHex.getBytes());
//公钥加密然后转Base64
String encMainKeyBase64 = Base64Util.base64Encode(encrypt);
System.out.println("证书公钥加密转base64:" + encMainKeyBase64);
//私钥解密
String encMainKey = Base64Util.base64Decode(encMainKeyBase64);
byte[] privateKeyData = Forms.hexStringToByte(Base64Util.base64Decode(workCertPrivateKey));
//创建PKCS8编码密钥规范
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyData);
//根据PKCS8编码密钥规范产生私钥对象
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
//用私钥初始化此Cipher对象(解密模式)
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//对数据解密
byte[] decrypt = cipher.doFinal(Forms.hexStringToByte(encMainKey));
System.out.println("证书公钥对应的私钥解密:" + new String(decrypt));
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(privateKey);
signature.update(HexUtil.decodeHex(mainKeyHex));
byte[] signData = signature.sign();
System.out.println(String.format("签名校验 --- genRsaSign RSAWithSHA256 签名: [%s]", Forms.byteToHexString(signData)));
String signBase64 = Base64Util.base64Encode(signData);
System.out.println(String.format("签名校验 --- genRsaSign RSAWithSHA256 签名 Base64: [%s]", signBase64));
signature.initVerify(publicKey);
String signBase64Decode = Base64Util.base64Decode(signBase64);
System.out.println(String.format("签名校验 --- genRsaSign RSAWithSHA256 验签 signBase64Decode: [%s]", signBase64Decode));
//update原待签名数据
signature.update(HexUtil.decodeHex(mainKeyHex));
//verify原签名后数据
boolean result = signature.verify(Forms.hexStringToByte(signBase64Decode));
System.out.println(String.format("签名校验 --- genRsaSign RSAWithSHA256 验签: [%s]", result));
} catch (Exception e) {
e.printStackTrace();
}
}