使用java进行SSL证书的签名与签验

一、签名与签验的含义

签名:客户端使用私钥对字符串加密,得到一个加密后的字符串

签验(签名验证):服务端使用公钥对字符串加密,验证加密后的字符串是否和客户端签名后一样

二、创建私钥、证书等文件

在linux中执行一下命令,生成所需的各个文件

1.手动生成私钥pri.key
openssl genrsa -des3 -passout pass:123456 -out pri.key 20482.生成pkcs8
openssl pkcs8 -topk8 -in pri.key -out pkcs8.pem -nocrypt -passin pass:1234563.生成pkcs10申请书P10.key //C国家,ST省,L市,O组织,OU部门,CN域名或ip
openssl req -new -key pri.key -passin pass:123456 -out P10.key -subj "/C=CN/ST=GUANGZHOU/L=GUANGZHOU/O=A/OU=B/CN=xxx"4.生成公钥pub.key
openssl rsa -in pri.key -passin pass:123456  -pubout -out pub.key5.生成证书server.crt
openssl x509 -req -days 365 -in P10.key -signkey pri.key -passin pass:123456  -out server.crt6.生成pk12文件
openssl pkcs12 -export -in server.crt -inkey pri.key -passin pass:123456 -passout pass:123456 -out tomcat.p127.生成keystore文件
keytool -importkeystore -v -srckeystore tomcat.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore tomcat.keystore -deststoretype jks -deststorepass 123456

以上生成的文件,我们需要用到pkcs8和server.crt

 生成后的pkcs8内容如下

-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7Uv7cOJ0oCsKV
Ous6SL9GcYUlfuuZUJfJVggCYU6peL53ZKv/DzP6FSQMO8W+VRFT+nBDpLntsMBd
mnH2hP0Jz/06d8RCJhRQxn2l8/4lqAZwFhyWvd+r4bkp6nfnY4sk0/ymtC2tp31l
WgTjXKIVtMfO9Km0bNxPIV206YdC9G9IIR65NSx8AyOCmwOp3mc028+9HE/OnT5s
mw2mJB5txVUUeVZYcOq0jaNs5Z/cwzOLuaZEcTPxLHoUAbJ0cDncO6j55/8r+zZg
hkois20k4kGVDUSYKeTH2MGKc62PJVyWRYgaaE+MuU+3tGgGW+/QWR/qShpOj/VA
QDSCVM1BAgMBAAECggEBAI8T6Hg6gccY2OD03MBq2jYq9QDbvYYf6Z+tp3Zx7oxB
HnHBIiIx8YhdZ2g0q2giP5b+HYt9IUpsi7GzCsK5dzBsfcWPvwarYS8FPOlpwL/w
Y+Ju8S0uH86AHVbnsOe6v4fEpyCJVK5j1MJ6DGvA0Eh2CXuIoqqdz3RdTt6k1FyN
Wmr5xYwVq/fnG7HECWpLT1RiZZjqqU7YHlJhdRfIHLEFxsuTrBeEcl2iAt6OTYgw
4QIVJ7DQ5n7cGd2nBgodM081dA8zNrJ5833alPI04pLiu7zNM4fewZuS1ThlnG2G
rJl07IeLjZSwz9dX6Ior/xihOkOqrOqf2rQgnXJQaLECgYEA38k6ijgdGizsXWhU
qeo1TY9XzTF7kh6FWFGNsBtlPC+UmOotkF590ApitZbVnVMg1h4ruruM5Z96K/w2
jL2WahwKjTH2pT1VmsPwXra5RgWPQTEH86VD8DQvYGoIxrqaD8zlgCZuCaj6BCNZ
k6jtsV+zsK2lUUNvoVDHzm7yymUCgYEA1kobLTlu469rSE4P8KP768TWE74mQrRw
LxUTKuUnI4jr3QmMLYMDATS6pRbeUKi/KryKuZB3zqZ0qQYuUAnWVOvwjQ0qsm87
+Y9ZivGtggSGP1fpJUnuA0svvWu+u+qjB3AUO+kdXaTKMNjRySYwk2yRjmiGCTRN
N+eeUT79+60CgYEA2j9bGy/MmhmTzykP4MJsh56zh4eptwCTFWY747NkEMVqi5Nf
KnwtATcPu39GB2/qB0hXZ36/07WrpbgMz0eaqRN96uP+YnqlwRE0nHphyHtlkbOq
yKC12E5cog/nTUDxPVWbwVY5XKxyYJTFKK/IWIM4MKe5Ib694Lpyks47dM0CgYAI
89uizg118bQ3txsvYIp71SNke8M5cjRloynD1wMFlwjTmTyEAj8z47IQh54OHJ6v
+GSLUB/RQF8LWLjCm0abEahhHDW2crM8v+JrzaEEF5BMQS36YtZQyOlbro6vZyQc
gXA2+IxDh0jzNXu+PZw17IlAO9mttMOtHD4fAtS1mQKBgQCg3fuw4KyZcw4XaCCN
KlAYJhnQ5Xk79XL6xAjRyKsSG7GfE1V4tvgkHnoxnAAIPeIqxqACi/VhGXSyxH2/
cfHuXtbdcPJ5XfBnloHOZX7PLEB70SIjxy2/L1lPJUh7e7onc9QVj+PB5Wf7j3J+
bqBhY+Rk1pJ9/9Z5zU7eIN/YcA==
-----END PRIVATE KEY-----

server.crt内容如下

-----BEGIN CERTIFICATE-----
MIIDMjCCAhoCCQCs0EkSIt7T/jANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJD
TjESMBAGA1UECAwJR1VBTkdaSE9VMRIwEAYDVQQHDAlHVUFOR1pIT1UxCjAIBgNV
BAoMAUExCjAIBgNVBAsMAUIxDDAKBgNVBAMMA3h4eDAeFw0yMjAxMDcwMzI0MzNa
Fw0yMzAxMDcwMzI0MzNaMFsxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHVUFOR1pI
T1UxEjAQBgNVBAcMCUdVQU5HWkhPVTEKMAgGA1UECgwBQTEKMAgGA1UECwwBQjEM
MAoGA1UEAwwDeHh4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1L+
3DidKArClTrrOki/RnGFJX7rmVCXyVYIAmFOqXi+d2Sr/w8z+hUkDDvFvlURU/pw
Q6S57bDAXZpx9oT9Cc/9OnfEQiYUUMZ9pfP+JagGcBYclr3fq+G5Kep352OLJNP8
prQtrad9ZVoE41yiFbTHzvSptGzcTyFdtOmHQvRvSCEeuTUsfAMjgpsDqd5nNNvP
vRxPzp0+bJsNpiQebcVVFHlWWHDqtI2jbOWf3MMzi7mmRHEz8Sx6FAGydHA53Duo
+ef/K/s2YIZKIrNtJOJBlQ1EmCnkx9jBinOtjyVclkWIGmhPjLlPt7RoBlvv0Fkf
6koaTo/1QEA0glTNQQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBk3o9BIggMhKdX
+wEIJ3piMnwXudLC2rUs2gP4EUaW/JuOotxVB+fRGNEBeHWyx8EupKEna5ho0IuN
WoqlFKyd7luqjUItbNyQ6IWoG8+bemhXWC6zg2jrPpHvwbhb9625Oo28XgNA5lsl
Lf+ybF7wpW7gMzLVK3z+vzOnvaunbjtASde7bLmL25qfm1K08iBQUOk0+EUCXY4q
efkRrVTh1aWwVSN51QhM3lMn0wGkPl2ww34zik5dA5RzXMlrOmHoGxzmd8oAJf7g
lJIQPrFyMv+mSo54TECkWSJeRAZNa+KZ0DKskcBPgE4SDZTlkCrZa3kEganTkE5F
/SO9kc0D
-----END CERTIFICATE-----

三、在Java代码中读取这些文件,然后调用对应类进行签名和签验

public class SslUtil {/*** 签验,返回签验是否通过* @param certificate   证书* @param signStr       签名的字符串* @param unSignStr     原字符串* @return* @throws NoSuchAlgorithmException* @throws InvalidKeyException* @throws SignatureException*/public static boolean verifySign(X509Certificate certificate, String signStr, String unSignStr)throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {byte[] data = Base64.getDecoder().decode(signStr);Signature signature = Signature.getInstance(certificate.getSigAlgName());signature.initVerify(certificate.getPublicKey());signature.update(unSignStr.getBytes(StandardCharsets.UTF_8));return signature.verify(data);}/*** 使用私钥对字符串进行签名,返回Base64编码后的字符串* @param str   原始字符串* @param privateKey    私钥* @param certificate   公钥* @return* @throws NoSuchAlgorithmException* @throws InvalidKeyException* @throws SignatureException*/public static String signString(String str, PrivateKey privateKey, X509Certificate certificate)throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {Signature signature = Signature.getInstance(certificate.getSigAlgName());signature.initSign(privateKey);signature.update(str.getBytes(StandardCharsets.UTF_8));byte[] data = signature.sign();return Base64.getEncoder().encodeToString(data);}/*** 读取证书文件* @param certFile* @return* @throws CertificateException*/public static X509Certificate getCertificate(String certFile) throws CertificateException, IOException {byte[] cert = readCertFile(certFile);CertificateFactory factory = CertificateFactory.getInstance("X.509");InputStream inputStream = new ByteArrayInputStream(cert);X509Certificate certificate = (X509Certificate) factory.generateCertificate(inputStream);return certificate;}/*** 读取私钥文件* @param pkcs8File* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static PrivateKey getPrivateKey(String pkcs8File)throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {byte[] prikey = readPkcs8File(pkcs8File);KeyFactory keyFactory = KeyFactory.getInstance("RSA");PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prikey);PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;}/*** 读取私钥文件,返回字节数组* 去掉开头结尾注释行,保留中间的数据* @param pkcs8File* @return* @throws IOException*/private static byte[] readPkcs8File(String pkcs8File) throws IOException {StringBuilder stringBuilder = new StringBuilder();try(BufferedReader br = new BufferedReader(new FileReader(pkcs8File))){String line;while((line=br.readLine())!=null){if(line.startsWith("-")){continue;}stringBuilder.append(line);}return Base64.getDecoder().decode(stringBuilder.toString());}}/*** 读取Cert证书文件,返回字节数组* 把命令生成的证书文件全部读取到内存* @param certFile* @return* @throws IOException*/private static  byte[] readCertFile(String certFile) throws IOException{try(InputStream is = new FileInputStream(certFile)){return is.readAllBytes();}}
}

调用方法验证,最后会返还true

public class Main {public static void main(String[] args) {String pkcs8File = "pkcs8.pem";String certFile = "server.crt";String str = "这是原文字符串";try{PrivateKey privateKey = SslUtil.getPrivateKey(pkcs8File);X509Certificate certificate = SslUtil.getCertificate(certFile);String signStr = SslUtil.signString(str, privateKey, certificate);System.out.println("字符串 ["+str+"] ---签名--->["+signStr+"]");boolean result = SslUtil.verifySign(certificate, signStr, str);System.out.println("签验结果:"+result);}catch (Exception e){e.printStackTrace();}}
}

四、不使用java来签名,而是使用openssl命令来生成签名,然后使用java来签验

openssl签名的命令

1.创建需要签名的文件
echo "这是原文字符串" > a.txt2.对文件进行签名
openssl dgst -sha256 -passin pass:123456 -sign pri.key a.txt > b.txt3.对签名后的文件进行base64编码
base64 b.txt > c.txt/**
上面需要注意的是,不能使用openssl rsautl来生成签名
因为rsault生成的签名结果,只包含字符串body,而使用dgst生成的签名结果,是一个结构体,包含了结构体的信息(类型、长度、body)
java的Signature类解析会按照结构体进行解析,如果去解析rsault生成签名就会因为结构不对
报错javax.crypto.BadPaddingException: Decryption error
*/

命令生成的c.txt内容如下

BTG52NfjVx6v7yHq4OzCbuzeA4bvlWSPHI4N4yHSDzErmB8nWsQsJ97fKOTbeH7pdLzTuO2RS832
3wbEBo+FFTyOgUENa0J+I22SVVmCAzuPGzATRdvk6rM7kTbeMJZSIiBq/v4hX6btvAOuJ0f0/ZG5
m1bbbr3SZe26DiNl7TKgWpMhFW2OyRmaTq6fPfhOx+1/zWHFiMWrVl5WO3vJpRt2We3/wRwNzgBj
e6M+BlcQvND+gSS//ix/6avvAaNjudXU8r8/T0MKMAsuRk0zPbOjqbNudZrN4aDRf3GUkYiFzp86
5F9kkyqhapISVnpNWDEpSQz14cyoDAFUcd0yDw==

然后Java中直接拿到这个字符串进行签验

public class Main {public static void main(String[] args) {String pkcs8File = "pkcs8.pem";String certFile = "server.crt";//注意,文件方式加密的字符串,后面多了一个回车符号String str = "这是原文字符串\n";try{X509Certificate certificate = SslUtil.getCertificate(certFile);String signStr = "BTG52NfjVx6v7yHq4OzCbuzeA4bvlWSPHI4N4yHSDzErmB8nWsQsJ97fKOTbeH7pdLzTuO2RS8323wbEBo+FFTyOgUENa0J+I22SVVmCAzuPGzATRdvk6rM7kTbeMJZSIiBq/v4hX6btvAOuJ0f0/ZG5m1bbbr3SZe26DiNl7TKgWpMhFW2OyRmaTq6fPfhOx+1/zWHFiMWrVl5WO3vJpRt2We3/wRwNzgBje6M+BlcQvND+gSS//ix/6avvAaNjudXU8r8/T0MKMAsuRk0zPbOjqbNudZrN4aDRf3GUkYiFzp865F9kkyqhapISVnpNWDEpSQz14cyoDAFUcd0yDw==";boolean result = SslUtil.verifySign(certificate, signStr, str);System.out.println("签验结果:"+result);}catch (Exception e){e.printStackTrace();}}
}

本文链接:https://my.lmcjl.com/post/1620.html

展开阅读全文

4 评论

留下您的评论.