java.security 框架之签名、加密、摘要及证书

2022-11-29  本文已影响0人  分布式与微服务

和第三方系统对接时,需要对隐私数据进行加密,对请求报文进行签名等。加密算法分为单向加密、对称加密、非对称加密等,其对应的算法也各式各样。Java 提供了统一的框架(java.security.*)来规范安全加密。下面将一一介绍以下内容。

一、加密算法概念及分类

常用的加密算法类型有三种,如下:

二、密钥生成
2.1 对称密钥生成

KeyGenerator 中的几个重要的方法如下。

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=
    }

2.2 非对称密钥生成

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==
    }

2.3 密钥 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)
    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==
    }

    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(太多了,选择性列举几个)。

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

六、Certificate - 证书存储

示例如下。

import java.io.InputStream;
import java.security.cert.CertificateFactory;

    // certificateStream 是证书的输入流
    public static PublicKey getPublicKeyByCer(InputStream certificateStream) throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
        Certificate certificate = (Certificate) certificateFactory.generateCertificate(certificateStream);
        return certificate.getPublicKey();
    }

七、KeyStore - 密钥证书的实体类
八、java.https 加载证书的 API

KeyManagerFactory、TrustManagerFactory => KeyManager、TrustManager => SSLContext => SSLEngine、SSLSocketFactory、SSLSocket

一般的证书加载过程:

  1. 用 Certificate、KeyStore 生成创建 KeyManagerFactory 和 TrustManagerFactory
  2. KeyManagerFactory 和 TrustManagerFactory 用来创建 KeyManager 和 TrustManager
  3. KeyManager 和 TrustManager 用来初始化 SSLContext
  4. 然后使用 SSLContext,创建实际实现 SSL/TLS 协议的对象(SSLEngine、SSLSocketFactory 或者 SSLSocket)
  5. SSLSocket 和 SSLEngine 可以直接在通信对象中使用
  6. KeyManager 和 TrustManager 作用:
    • KeyManager 负责向对等端显示使用的凭证(使用的密码标准、加密算法、证书、公钥、签名等)
    • TrustManager 负责验证从对等端收到的凭证,验证凭证有多种方式,其中之一是创建 CertPath 对象,并让 JDK 的内置公钥基础结构(PKI)框架处理验证。 在内部,CertPath 实现可能会创建一个 Signature 对象,并使用它来验证证书链中的每个签名。
上一篇 下一篇

猜你喜欢

热点阅读