一、签名与签验的含义
签名:客户端使用私钥对字符串加密,得到一个加密后的字符串
签验(签名验证):服务端使用公钥对字符串加密,验证加密后的字符串是否和客户端签名后一样
二、创建私钥、证书等文件
在linux中执行一下命令,生成所需的各个文件
1.手动生成私钥pri.key openssl genrsa -des3 -passout pass:123456 -out pri.key 2048 2.生成pkcs8 openssl pkcs8 -topk8 -in pri.key -out pkcs8.pem -nocrypt -passin pass:123456 3.生成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.key 5.生成证书server.crt openssl x509 -req -days 365 -in P10.key -signkey pri.key -passin pass:123456 -out server.crt 6.生成pk12文件 openssl pkcs12 -export -in server.crt -inkey pri.key -passin pass:123456 -passout pass:123456 -out tomcat.p12 7.生成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 {
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);
}
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);
}
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;
}
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;
}
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());
}
}
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.txt 2.对文件进行签名 openssl dgst -sha256 -passin pass:123456 -sign pri.key a.txt > b.txt 3.对签名后的文件进行base64编码 base64 b.txt > c.txt
命令生成的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();
}
}
}



