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

一、签名与签验的含义

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

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

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

在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 {

    /**
     * 签验,返回签验是否通过
     * @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.txt

2.对文件进行签名
openssl dgst -sha256 -passin pass:123456 -sign pri.key a.txt > b.txt

3.对签名后的文件进行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();
        }
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在JavaMail中使用签名证书进行签名,您需要执行以下步骤: 1.生成自签名证书。您可以使用Java的keytool工具生成自签名证书。例如,以下命令可以生成一个名为“mycert”的自签名证书: ``` keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650 ``` 2.将自签名证书添加到Java密钥库中。您可以使用以下命令将自签名证书添加到名为“mykeystore”的Java密钥库中: ``` keytool -importkeystore -srckeystore keystore.p12 -srcstoretype PKCS12 -destkeystore mykeystore -deststoretype JKS -deststorepass changeit ``` 3.在JavaMail中使用签名证书进行签名。以下是一个使用签名证书签名邮件的示例代码: ```java import javax.mail.*; import javax.mail.internet.*; import java.security.Security; import java.util.Properties; public class Mail { public static void main(String[] args) throws Exception { // 设置 Java 安全属性 Security.setProperty("ssl.SocketFactory.provider", "com.sun.net.ssl.internal.ssl.Provider"); Security.setProperty("ssl.SocketFactory.factoryClass", "javax.net.ssl.SSLSocketFactory"); // 创建 JavaMail 会话 Properties props = new Properties(); props.put("mail.smtp.host", "smtp.gmail.com"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.port", "465"); props.put("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); Session session = Session.getInstance(props, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("username", "password"); } }); // 创建邮件消息 MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("from@example.com")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com")); message.setSubject("Subject"); // 创建多部分消息 Multipart multipart = new MimeMultipart(); // 创建文本部分 MimeBodyPart textPart = new MimeBodyPart(); textPart.setText("Body"); // 创建签名部分 MimeBodyPart signaturePart = new MimeBodyPart(); signaturePart.setContent("Signature", "application/pgp-signature"); // 将文本部分和签名部分添加到多部分消息中 multipart.addBodyPart(textPart); multipart.addBodyPart(signaturePart); // 设置多部分消息为邮件消息的内容 message.setContent(multipart); // 发送邮件消息 Transport.send(message); } } ``` 在此示例代码中,我们使用了自签名证书进行签名,将签名部分作为多部分消息的一部分添加到消息中,然后将多部分消息设置为邮件消息的内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值