一、签名与签验的含义
签名:客户端使用私钥对字符串加密,得到一个加密后的字符串
签验(签名验证):服务端使用公钥对字符串加密,验证加密后的字符串是否和客户端签名后一样
二、创建私钥、证书等文件
在linux中执行一下命令,生成所需的各个文件
1.手动生成私钥
openssl genrsa -des3 -passout pass:123456 -out 2048
2.生成pkcs8
openssl pkcs8 -topk8 -in -out -nocrypt -passin pass:123456
3.生成pkcs10申请书 //C国家,ST省,L市,O组织,OU部门,CN域名或ip
openssl req -new -key -passin pass:123456 -out -subj "/C=CN/ST=GUANGZHOU/L=GUANGZHOU/O=A/OU=B/CN=xxx"
4.生成公钥
openssl rsa -in -passin pass:123456 -pubout -out
5.生成证书
openssl x509 -req -days 365 -in -signkey -passin pass:123456 -out
6.生成pk12文件
openssl pkcs12 -export -in -inkey -passin pass:123456 -passout pass:123456 -out tomcat.p12
7.生成keystore文件
keytool -importkeystore -v -srckeystore tomcat.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore -deststoretype jks -deststorepass 123456
以上生成的文件,我们需要用到pkcs8和
生成后的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-----
内容如下
-----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 = ().decode(signStr);
Signature signature = (());
(());
((StandardCharsets.UTF_8));
return (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 = (());
(privateKey);
((StandardCharsets.UTF_8));
byte[] data = ();
return ().encodeToString(data);
}
/**
* 读取证书文件
* @param certFile
* @return
* @throws CertificateException
*/
public static X509Certificate getCertificate(String certFile) throws CertificateException, IOException {
byte[] cert = readCertFile(certFile);
CertificateFactory factory = ("X.509");
InputStream inputStream = new ByteArrayInputStream(cert);
X509Certificate certificate = (X509Certificate) (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 = ("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prikey);
PrivateKey privateKey = (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=())!=null){
if(("-")){
continue;
}
(line);
}
return ().decode(());
}
}
/**
* 读取Cert证书文件,返回字节数组
* 把命令生成的证书文件全部读取到内存
* @param certFile
* @return
* @throws IOException
*/
private static byte[] readCertFile(String certFile) throws IOException{
try(InputStream is = new FileInputStream(certFile)){
return ();
}
}
}
调用方法验证,最后会返还true
public class Main {
public static void main(String[] args) {
String pkcs8File = "";
String certFile = "";
String str = "这是原文字符串";
try{
PrivateKey privateKey = (pkcs8File);
X509Certificate certificate = (certFile);
String signStr = (str, privateKey, certificate);
("字符串 ["+str+"] ---签名--->["+signStr+"]");
boolean result = (certificate, signStr, str);
("签验结果:"+result);
}catch (Exception e){
();
}
}
}
四、不使用java来签名,而是使用openssl命令来生成签名,然后使用java来签验
openssl签名的命令
1.创建需要签名的文件
echo "这是原文字符串" >
2.对文件进行签名
openssl dgst -sha256 -passin pass:123456 -sign >
3.对签名后的文件进行base64编码
base64 >
/**
上面需要注意的是,不能使用openssl rsautl来生成签名
因为rsault生成的签名结果,只包含字符串body,而使用dgst生成的签名结果,是一个结构体,包含了结构体的信息(类型、长度、body)
java的Signature类解析会按照结构体进行解析,如果去解析rsault生成签名就会因为结构不对
报错: Decryption error
*/
命令生成的内容如下
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 = "";
String certFile = "";
//注意,文件方式加密的字符串,后面多了一个回车符号
String str = "这是原文字符串\n";
try{
X509Certificate certificate = (certFile);
String signStr = "BTG52NfjVx6v7yHq4OzCbuzeA4bvlWSPHI4N4yHSDzErmB8nWsQsJ97fKOTbeH7pdLzTuO2RS8323wbEBo+FFTyOgUENa0J+I22SVVmCAzuPGzATRdvk6rM7kTbeMJZSIiBq/v4hX6btvAOuJ0f0/ZG5m1bbbr3SZe26DiNl7TKgWpMhFW2OyRmaTq6fPfhOx+1/zWHFiMWrVl5WO3vJpRt2We3/wRwNzgBje6M+BlcQvND+gSS//ix/6avvAaNjudXU8r8/T0MKMAsuRk0zPbOjqbNudZrN4aDRf3GUkYiFzp865F9kkyqhapISVnpNWDEpSQz14cyoDAFUcd0yDw==";
boolean result = (certificate, signStr, str);
("签验结果:"+result);
}catch (Exception e){
();
}
}
}