加密和密钥管理(Cryptography and Key Management)

在介绍具体的API前,我们先熟悉下相关知识

基础知识

对称加密

对称加密是指双方持有相同的密钥进行通信,加密速度快,但是有一个安全问题,双方怎样获得相同的密钥?你总不能总是拿着U盘把密钥拷贝给对方吧。 常见的对称加密算法有DES、3DES、AES等

非对称加密

非对称加密,又称为公开密钥加密,是为了解决对称加密中的安全问题而诞生,一个称为公开密钥(public key),即公钥,另一个称为私钥(private key),即私钥。但是它的加密速度相对于对称加密来说很慢。

  • 公钥(public key)是对外开放的,私钥(private key)是自己拥有的。

  • 公钥(public key)加密的数据,只能用私钥(private key)解密。

  • 私钥(private key)加密的数据,只能用公钥(public key)解密。

信息安全问题

在信息安全性问题中,我们常常要做到三点才能保证信息的安全:

  1. 信息的保密性

  2. 信息的完整性

  3. 身份识别

信息的保密性(加密算法)

信息的保密性我们可以使用对称加密和非对称加密来完成,使用对称加密来完成,速度相对非对称加密很快,但是存在一个安全问题,密钥如何传递?由此通用的方法是使用非对称加密+对称加密来完成。客户端使用公钥对对称加密的密钥进行加密,然后传递给服务端,服务端使用私钥进行解密确认密钥,开始传输数据。

 

信息的完整性(数字签名)

信息传输的途中,我们的信息很有可能被第三方劫持篡改,所以我们需要保证信息的完整性,通用方法是使用散列算法如SHA1,MD5将传输内容hash一次获得hash值,即摘要。客户端使用服务端的公钥对摘要和信息内容进行加密,然后传输给服务端,服务端使用私钥进行解密获得原始内容和摘要值,这时服务端使用相同的hash算法对原始内容进行hash,然后与摘要值比对,如果一致,说明信息是完整的。

身份识别(数字证书)

在信息传输的过程中,我们通常需要验证信息的发送方的身份,这时我们转化一下思路就可以完成,把发送端的公钥发送给接收端,发送端通过把自己的内容使用私钥加密然后发送给接收端,接收端只能用发送端的公钥解密,自然就验证了发送端的身份。

 

数字证书

在传输的过程中,客户端如何获得服务器端的公钥呢?当时是服务器分发给客户端,如果一开始服务端发送的公钥到客户端的过程中有可能被第三方劫持,然后第三方自己伪造一对密钥,将公钥发送给客户端,当服务器发送数据给客户端的时候,中间人将信息进行劫持,用一开始劫持的公钥进行解密后,然后使用自己的私钥将数据加密发送给客户端,而客户端收到后使用公钥解密,反过来亦是如此,整个过程中间人是透明的,但信息泄露却不得而知。

 

 为了防止这种情况,数字证书就出现了,它其实就是基于上上面所说的私钥加密数据,公钥解密来验证其身份。 数字证书是由权威的CA(Certificate Authority)机构给服务端进行颁发,CA机构通过服务端提供的相关信息生成证书,证书内容包含了持有人的相关信息,服务器的公钥,签署者签名信息(数字签名)等,最重要的是公钥在数字证书中。 数字证书是如何保证公钥来自请求的服务器呢?数字证书上由持有人的相关信息,通过这点可以确定其不是一个中间人;但是证书也是可以伪造的,如何保证证书为真呢? 一个证书中含有三个部分:"证书内容,散列算法,加密密文",证书内容会被散列算法hash计算出hash值,然后使用CA机构提供的私钥进行RSA加密。

 

当客户端发起请求时,服务器将该数字证书发送给客户端,客户端通过CA机构提供的公钥对加密密文进行解密获得散列值(数字签名),同时将证书内容使用相同的散列算法进行Hash得到另一个散列值,比对两个散列值,如果两者相等则说明证书没问题。

 

一些常见的证书文件类型如下:

X.509#DER 二进制格式证书,常用后缀.cer .crt X.509#PEM 文本格式证书,常用后缀.pem 有的证书内容是只包含公钥(服务器的公钥),如.crt、.cer、.pem 有的证书既包含公钥又包含私钥(服务器的私钥),如.pfx、.p12

数字证书的补充知识点

  1. 证书结构

    • 公钥证书:包含实体的公钥、实体的身份信息以及证书颁发机构(CA,Certificate Authority)的签名。它用于加密通信、数字签名验证等。

    • 私钥证书:类似于公钥证书,但包含了实体的私钥,通常用于数字签名生成和加密解密等操作。

  2. 证书内容

    • 主题(Subject):证书所代表的实体,如个人、组织、服务器等。

    • 颁发者(Issuer):签署证书的CA的信息。

    • 公钥:用于加密通信、数字签名验证等的公钥。

    • 有效期(Validity):证书的生效日期和截止日期。

    • 数字签名:颁发者对证书内容的签名,用于验证证书的真实性和完整性。

  3. 数字签名

    • 数字签名使用颁发者的私钥对证书内容进行加密,以证明证书的合法性。

    • 验证数字签名时,可以使用颁发者的公钥来解密签名,然后与证书内容进行比对,以确保证书未被篡改。

  4. 证书链

    • CA通常也会持有自己的证书。一个证书链是一系列的证书,其中最后一个证书由根CA签发,而其他证书都是由其他CA签发的。

    • 这些证书的链式验证确保了证书的可信度和完整性。

  5. 证书撤销

    • 在某些情况下,证书可能需要被撤销,例如证书的私钥丢失或者实体不再受信任。

    • 撤销列表(CRL,Certificate Revocation List)和在线证书状态协议(OCSP,Online Certificate Status Protocol)是用于检查证书是否被撤销的机制。

  6. 证书颁发机构(CA)

    • CA是负责签发和管理数字证书的组织。根CA是信任链的根节点,可以被广泛信任。

    • 自签名证书是由自己颁发和签名的证书,不受根CA信任。

  7. SSL/TLS 通信

    • 数字证书在网站和浏览器之间建立安全的SSL/TLS加密通信。

    • 网站使用服务器证书来证明其身份,浏览器使用根CA证书来验证证书的真实性。

  8. 证书存储

    • 证书存储用于安全地保存数字证书和私钥,以防止未经授权的访问。

密钥生成

对称密钥生成

  • KeyGenerator(javax.crypto 包下)用于生成对称密钥。

  • 支持算法:AES、ARCFOUR、DES、DESede、HmacMD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、RC2 等。

public static final KeyGenerator getInstance(String algorithm, String provider)
public static final KeyGenerator getInstance(String algorithm)
public final void init(int keysize)
public final void init(int keysize, SecureRandom random)
public final void init(SecureRandom random)
public final void init(AlgorithmParameterSpec params, SecureRandom random)
public final SecretKey generateKey()

示例:

public static void main(String[] args) throws Exception {
    KeyGenerator kg = KeyGenerator.getInstance("DES");
    SecureRandom random = new SecureRandom();
    random.nextBytes(new byte[128]);
    kg.init(56, random);
    SecretKey sk = kg.generateKey();
    byte[] b = sk.getEncoded();
    System.out.println(new String(Base64.encodeBase64(b)));  // XZdrC/in5uk=    
}

非对称密钥生成

  • KeyPairGenerator(java.security 包下) 用于生成非对称加密算法的密钥对 KeyPair(java.security 包下),KeyPair 会包括一个公钥和私钥。

  • 支持算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC 等。

KeyPairGenerator.java 和 KeyPair.java 类中的重要的方法如下所示

// KeyPairGenerator.java
public static KeyPairGenerator getInstance(String algorithm)
public static KeyPairGenerator getInstance(String algorithm, String provider)
public void initialize(int keysize, SecureRandom random)
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
public final KeyPair genKeyPair() 
// KeyPair.java
public PublicKey getPublic()
public PrivateKey getPrivate()

示例:

public static void main(String[] args) throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        random.nextBytes(new byte[516]);
        keyGen.initialize(516, random);
        KeyPair keyPair = keyGen.genKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] publicKeyEncoded = publicKey.getEncoded();
        byte[] privateKeyEncoded = privateKey.getEncoded();
        System.out.println(new String(Base64.encodeBase64(publicKeyEncoded)));
        // MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBCJQ3Ee/oxid0CkYxQaNyUlPlIJKFUuwB+kYAuZ5OdxJjSRHJ7jb931aIU+t61DhG2BBiegs3588SyGRe8IQZM10CAwEAAQ==
        System.out.println(new String(Base64.encodeBase64(privateKeyEncoded)));
        // MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEIlDcR7+jGJ3QKRjFBo3JSU+UgkoVS7AH6RgC5nk53EmNJEcnuNv3fVohT63rUOEbYEGJ6CzfnzxLIZF7whBkzXQIDAQABAkEFjPhQp7whMXe4ChBmmr0mHVf7ijGvJDpnVxGzB4VXL0+5TGT0fptb85dNjVmKD2REe0fBntRh7hSZETgYCiZMgQIhAwPbBuZ3QjDhMfx3fb89xCnLZFzEILzvKXeS1Q5xx/ehAiEC2Go0R13hndaDIzhq/Rn2fsVLzAlAFXIJBEdTVZ498j0CIQKYbA/JjjmVWBUubjH50RKuo54WWOKRoRLCEsyCraFkYQIgS4MnDEb1PrGgQqR0ouxwG1BEvVAwLoj12lWyk+ulrFkCIQJyg5t1WkvLVeqrGOGhj5jIkWEfxad/43X2fkFM6WyL3Q==
    }

密钥 Key 和密钥规格 KeySpec 的相互转化

KeySpec 是一个接口,用来组成密钥内容的规范。如果密钥存储在硬件设备上,则其规范可以包含有助于标识该设备上的密钥的信息。

KeySpec 具有规范性,所以一般会根据外部参数生成 KeySpec,再根据 KeySpec 生成对应的 Key。SecretKeyFactory、KeyFactory 的作用就是转换 Key 与 KeySpec。

SecretKeyFactory 用于对称加密的密钥和密钥规格之间的转换,配合 KeyGenerator 使用,支持算法:AES、ARCFOUR、DES、DESede、PBEWithMD5AndDES、PBEWithHmacSHA256AndAES_128、PBKDF2WithHmacSHA256 等。

SecretKeyFactory.java 中的几个方法如下所示。

public static final SecretKeyFactory getInstance(String algorithm)
public static final SecretKeyFactory getInstance(String algorithm, String provider)
public final SecretKey translateKey(SecretKey key)
public final SecretKey generateSecret(KeySpec keySpec)
public final KeySpec getKeySpec(SecretKey key, Class<?> keySpec)

代码示例:

	public static void main(String[] args) throws Exception {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        byte[] desKey = "hello world".getBytes(StandardCharsets.UTF_8); // 设置密钥
        DESKeySpec keySpec = new DESKeySpec(desKey);                    // 设置密钥参数
        SecretKey key = keyFactory.generateSecret(keySpec);             // 得到密钥对象
        byte[] b = key.getEncoded();
        System.out.println(new String(Base64.encodeBase64(b)));         // aGRtbW5XV1c=
    }

KeyFactory 用于非对称加密]的密钥和密钥规格之间的转换,配合 KeyPairGenerator 使用,支持算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC 等。

KeyFactory.java 中的几个方法如下所示。

// KeyFactory.java
public static KeyFactory getInstance(String algorithm)
public static KeyFactory getInstance(String algorithm, String provider)
public final PublicKey generatePublic(KeySpec keySpec)
public final PrivateKey generatePrivate(KeySpec keySpec)
public final <T extends KeySpec> T getKeySpec(Key key, Class<T> keySpec)

代码示例:

 public static void main(String[] args) throws Exception {
        // 生成 RSA 密钥对
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        random.nextBytes(new byte[2048]);
        keyGen.initialize(2048, random);
        KeyPair keyPair = keyGen.genKeyPair();

        // PublicKey 转 KeySpec; KeySpec 再转 PublicKey
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
        byte[] pubKeyEncoded = pubKey.getEncoded();
        System.out.println(new String(Base64.encodeBase64(pubKeyEncoded)));
        // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglncEo1/cWWMQzPxShtZY+zMERNcI6EHgEtYcPJaxF0mLVKk0af/5YB1WMHkEiq/CaRJa1GPS24iATIZpl3ICSqo8gSbgctogOxCU+XBtWhyVBgnaPpt4xoNFpoGyNyZzOvEq4YiNVDMhnTD+0Qlx7jyvV8R3xWcGWyZHB68KGwj9NvxHcsxkDZzXqAkDdiqdmqlImQoS9NBdRWQarX/rcBooQ9qs3yxu8i/bufVwwQS+lPvZoaKzJOvA4/qraa+ffXPJjsyLZk67C6ckQjmRbhgGuoa6fJ7FCRc98qZy9Cx/NxiqypHkIG0glgoOA4u1RIHBOEWY4Vo3G0d0Tw5PwIDAQAB

        // PrivateKey 转 KeySpec; KeySpec 再转 PrivateKey
        PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
        PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
        byte[] priKeyEncoded = priKey.getEncoded();
        System.out.println(new String(Base64.encodeBase64(priKeyEncoded)));
        // MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCCWdwSjX9xZYxDM/FKG1lj7MwRE1wjoQeAS1hw8lrEXSYtUqTRp//lgHVYweQSKr8JpElrUY9LbiIBMhmmXcgJKqjyBJuBy2iA7EJT5cG1aHJUGCdo+m3jGg0WmgbI3JnM68SrhiI1UMyGdMP7RCXHuPK9XxHfFZwZbJkcHrwobCP02/EdyzGQNnNeoCQN2Kp2aqUiZChL00F1FZBqtf+twGihD2qzfLG7yL9u59XDBBL6U+9mhorMk68Dj+qtpr599c8mOzItmTrsLpyRCOZFuGAa6hrp8nsUJFz3ypnL0LH83GKrKkeQgbSCWCg4Di7VEgcE4RZjhWjcbR3RPDk/AgMBAAECggEBAIB/TsfnPvOtNEjnQnxYW5V60Gwg1pq02i0pmUS2VK3wWXsiViHraAJ40LUvZcJW6z34+vtVSloEdncRSWHMXy5SJHt3+UhJGXrF7FjCTGOlU9b8fJUrEfpnKvHV4sxNUzxESvr/XmeKgCQnpS7kLg4ljv0JZBezOM+DU6f50GhTSWJeTGzCOfGl6xXjpLeeYnnbEtCKG5qo3YH3wAeFF0Zvb6Rqm0pXJwQ/YMUNFG/2CoAktKtUfpF6DSbmFzzRA03nWqRrGaWVqtzCmQ7ndSWsdeG00RjNRkA94VevoHZV9G3rLgzPmfWF5nn1QT/SGAUAU+VmgoG0dcmDyivN91ECgYEA48zUx3Mw09NCl9TAofHFFAY6TdR/7dmThzTsEzJoCzyjvLOEUExMI8w6W2t5d4jYx3hxgQoK8mbZfn9vWrK9IEZEsQ25ec/6kqW+hpcwXTaN5aTWRUILJTz5yHothaEbK5sDSRTRFvEVlxszikmJfT6Z1/4rQr0mw+a4ZuOp61kCgYEAknzJunQYfLOvSnJocX9ms5sFkA+wFq9twHXptUTeh2wcxz0C2zRobsrlrW8CwWG5q9P90ywRXCaoM3fp5w+x8qPLmdzW/GNQC/F5o+d/SnNYMSdPr4OS14I3gu8zMO9eTWnLY8IYoxeXRl0/5/ewtM8WvsW/KrXErYjqf9elblcCfwHHl+H3BGqjO+Hzx418Vg3R/qKdBmLVUFG+GBoOSsHLt3vB60a1UeL1tX8BV/GXIBpu1nQrn+pE424ZkMUkoFWgNukrMkfBWDPNF6/1fms8Ad/JaeMgoPWphEoMqk5g89VjYKMxhnCncYO8sqph6LERzCHj2nKrB6KAKvCi1rECgYABCqYcj0rFSDnM27dmZzOBv25wscvcvW6YWb5Jra2vZNNnj0V/7YV4lDTB4PIyEdHSKPW7FKsi7ptvkkC1heUMBqIh+/IDZWliTFtDERhUnTFZWCA27UaUBbcDVVQV2v3eqwvpL64hKr/Gnk8gBSDaiEZvINTVJum5GiogspXYjQKBgAV2DXc2P4AHqt981ou3GPtFsAfcL3JjwslnIkgNk7nEReSTmsJ2rERrLUKQEIKLNdxBOrK/0AgsK8+ysFk/+lmVd9rGY3kROF4HRvV8aIoLGkKlpIjhNhTpmlWzfFrMXebtiK2iEfbZcC1wedl+V00Q9YntOYMlJPdfhl1wxN7H
    }

摘要算法 - MessageDigest 和 javax.crypto.Mac(HMAC)

  • 单向加密是不可逆的,MD5、SHA、MAC 都是单向加密算法,也称之为摘要算法。

  • MD5、SHA 会根据明文用哈希算法计算一个固定长度的摘要(哈希值),然后把明文和摘要发送给接收者,接收者根据同样的算法计算出摘要,对比两个摘要是否一样即可验证明文的正确性。它的应用场景是防止报文被篡改和校验数据。

  • MD5、SHA 等算法是开源的,容易被试探出来。有没有更安全的摘要算法呢?HMAC(带密钥的哈希函数),用一个密钥和一个明文消息作为输入,生成一个消息摘要。密钥一般使用KeyGenerator 创建,相当于一个密码值,其被试探出的概率小。

  • MessageDigest 支持的算法:MD2、MD5、SHA-1、SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

  • javax.crypto.Mac支持的算法:HmacMD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、PBEWithHmacSHA1。

MessageDigest 的示例如下所示。

public static void main(String[] args) throws Exception {
    MessageDigest digest = MessageDigest.getInstance("MD5");
    System.out.println(new String(Base64.encodeBase64(digest.digest("hello world!".getBytes()))));
    // /D/5joxqDTCH1RXARz+Gdw==
    System.out.println(new String(Base64.encodeBase64(digest.digest("hello happy!".getBytes()))));
    // mfzFDfIxtbUOJRN7WGSNVA==
}

MAC 的示例如下所示。

	public static void main(String[] args) throws Exception {
        // 初始化 HmacMD5 摘要算法的密钥产生器
        KeyGenerator generator = KeyGenerator.getInstance("HmacMD5");
        // 产生密钥
        SecretKey secretKey = generator.generateKey();
        // SecretKeySpec 继承于 SecretKey 和 KeySpec,因此可直接用 SecretKeySpec 初始化 Mac
        // SecretKey secretKey = new SecretKeySpec("password".getBytes(), "HmacMD5");
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(secretKey);
        // 计算摘要
        String data = "hello world";
        byte[] result1 = mac.doFinal(data.getBytes());
        System.out.println(new String(Base64.encodeBase64(result1)));
    }

签名算法工具 - Signature

签名后的数据具有唯一标识性,就像一个人的签名能代表一个人的身份。签名一般是指用非对称加密算法的私钥来加密明文的过程,生成的密文可以被持有公钥的人识别解密,只要你的公钥是准确无误的,就能保证你解密的数据是来自持有私钥的一方。

如何保证公钥是正确无误,没被篡改的?

  1. 一对一给你;

  2. 获取公钥后通过权威机构认证。

支持算法:NONEwithRSA、MD2withRSA、MD5withRSA、SHA512/224withRSA、SHA512/256withRSA、RSASSA-PSS、NONEwithDSA、SHA512withDSA、NONEwithECDSA、SHA512withECDSA、MD5withRSAandMGF1(太多了,此处选择性列举几个)

Signature 配合 KeyPairGenerator 使用,进行数据签名和验签的 demo 如下所示。

public static void main(String[] args) throws Exception {
        // 生成 RSA 密钥对
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        random.nextBytes(new byte[1024]);
        keyGen.initialize(1024, random);
        KeyPair keyPair = keyGen.genKeyPair();

        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(keyPair.getPrivate());
        // 待签名字符串
        String content = "hello world";
        byte[] data = content.getBytes("UTF-8");
        // 数据签名
        signature.update(data);
        byte[] digest = signature.sign();
        Base64.Encoder encoder = Base64.getEncoder();
        System.out.println("签名后的字符串:" + encoder.encodeToString(digest));

        // 数据验签
        signature.initVerify(keyPair.getPublic());
        signature.update(data);
        System.out.println("验签结果:" + signature.verify(digest));
    }

常用加解密工具类 - Cipher

用于加密/解密数据。支持各种类型的算法:对称加密(例如 AES),非对称加密(例如RSA)。

支持算法:AES、AESWrap、ARCFOUR、Blowfish、DES、DESede、DESedeWrap、ECIES、RSA(太多了,选择性列举几个)。

加密案例:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class CipherExample {
    public static void main(String[] args) throws Exception {
        String plaintext = "你好,这是一个秘密消息!";
        
        // 生成用于加密的秘密密钥
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);  // 密钥长度(以位为单位)
        SecretKey secretKey = keyGenerator.generateKey();
        
        // 初始化用于加密的密码器
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        
        // 加密明文
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
        
        System.out.println("加密后: " + new String(encryptedBytes));
    }
}

解密案例:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class DecryptionExample {
    public static void main(String[] args) throws Exception {
        String encryptedText = "..."; // Replace with the actual encrypted text
        
        // Generate the same secret key used for encryption
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);
        SecretKey secretKey = keyGenerator.generateKey();
        
        // Initialize the cipher for decryption
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        
        // Decrypt the encrypted bytes
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
        
        String decryptedText = new String(decryptedBytes);
        System.out.println("Decrypted: " + decryptedText);
    }
}

其他示例见:RSA 加解密(Java 实现)

Certificate - 证书存储

生成证书

使用Java Keytool工具生成自签名证书并存储在密钥库中:
keytool -genkeypair -alias mycertificate -keyalg RSA -keysize 2048 -keystore keystore.jks

keytool其他相关命令

导出证书到文件:

keytool -exportcert -alias mykey -keystore keystore.jks -file mykey.crt

创建证书签名请求(CSR):

keytool -certreq -alias mykey -keystore keystore.jks -file mykey.csr

导入证书到密钥库:

keytool -importcert -alias mykey -file mykey.crt -keystore keystore.jks

查看密钥库内容:

keytool -list -keystore keystore.jks

使用Java编程方式生成 X.509 证书:
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class CertificateGenerationExample {
	public static void main(String[] args) throws Exception {
		Security.addProvider(new BouncyCastleProvider());

		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
		keyPairGenerator.initialize(2048);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();

		X509Certificate cert = generateSelfSignedCertificate(keyPair);

		try (FileOutputStream fos = new FileOutputStream("selfsigned.crt")) {
			fos.write(cert.getEncoded());
		}

		System.out.println("Certificate generated and saved as selfsigned.crt");
	}

	public static X509Certificate generateSelfSignedCertificate(KeyPair keyPair) throws Exception {
		Date startDate = new Date();
		Date endDate = new Date(startDate.getTime() + 365 * 24 * 60 * 60 * 1000L); // 1 year validity

		BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());

		X500Name issuer = new X500Name("CN=SelfSigned");
		X500Name subject = new X500Name("CN=SelfSigned");

		SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());

		X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
				issuer, serialNumber, startDate, endDate, subject, publicKeyInfo);

		certBuilder.addExtension(
				org.bouncycastle.asn1.x509.Extension.basicConstraints, true, new BasicConstraints(true));

		ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());

		X509CertificateHolder certHolder = certBuilder.build(contentSigner);

		return new JcaX509CertificateConverter().getCertificate(certHolder);
	}
}

依赖:

 <dependencies>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <!--
        X509V3CertificateGenerator已弃用。改用X509v3CertificateBuilder。
        需要导入一下依赖
        -->
        <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>

KeyStore-密钥证书的实体类

  • KeyStore用于存储私钥和证书(公钥在证书Certificate里面)

  • 公钥:是一个详细的实体的数字关联,并有意让所有想同这个实体发生信任关系的其他实体知道.公共钥匙用来检验签名;

  • 私钥:是一些数字,私有和公共钥匙存在所有用公共钥匙加密的系统的钥匙对中.公共钥匙用来加密数据,私有钥匙用来计算签名.公钥加密的消息只能用私钥解密,私钥签名的消息只能用公钥检验签名。

  • 示例

// 创建一个KeyStore对象并加载密钥库
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("mykeystore.jks"), "password".toCharArray());

// 从密钥库中获取一个密钥
Key key = keyStore.getKey("mykeyalias", "password".toCharArray());

// 使用密钥进行加密/解密等操作

生成X.509 证书保存到密钥库中

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.security.cert.Certificate;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class CertificateGenerationExample {
	public static void main(String[] args) throws Exception {
		Security.addProvider(new BouncyCastleProvider());

		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
		keyPairGenerator.initialize(2048);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();

		X509Certificate cert = generateSelfSignedCertificate(keyPair);

		KeyStore keyStore = KeyStore.getInstance("JKS");
		keyStore.load(null, null);

		keyStore.setKeyEntry("selfsigned", keyPair.getPrivate(), "password".toCharArray(), new Certificate[]{cert});

		try (FileOutputStream fos = new FileOutputStream("keystore.jks")) {
			keyStore.store(fos, "password".toCharArray());
		}
		
		System.out.println("Certificate and private key saved to keystore.jks");
	}

	public static X509Certificate generateSelfSignedCertificate(KeyPair keyPair) throws Exception {
		Date startDate = new Date();
		Date endDate = new Date(startDate.getTime() + 365 * 24 * 60 * 60 * 1000L); // 1 year validity

		BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());

		X500Name issuer = new X500Name("CN=SelfSigned");
		X500Name subject = new X500Name("CN=SelfSigned");

		SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());

		X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
				issuer, serialNumber, startDate, endDate, subject, publicKeyInfo);

		certBuilder.addExtension(
				org.bouncycastle.asn1.x509.Extension.basicConstraints, true, new BasicConstraints(true));

		ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());

		X509CertificateHolder certHolder = certBuilder.build(contentSigner);

		return new JcaX509CertificateConverter().getCertificate(certHolder);
	}
}

参考:

Java Platform SE 8

https://www.cnblogs.com/cscw/p/14532682.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello_中年人

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值