乐趣区

关于java:基础篇javasecurity框架之签名加密摘要及证书

前言

和前端进行数据交互时或者和第三方商家对接时,须要对隐衷数据进行加密。单向加密,对称加密,非对称加密,其对应的算法也各式各样。java 提供了对立的框架来标准(java.security)平安加密这类 API。上面将一一介绍

  • 加密算法概念及分类
  • 秘钥生成
  • 摘要算法工具 -MessageDigest
  • 签名算法工具 -Signature
  • 罕用加密工具类 -Cipher
  • Certificate- 证书的保留
  • KeyStore- 密钥证书的实体类
  • https 证书加载

关注公众号,一起交换;微信搜一搜: 潜行前行

1 加密算法概念及分类

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

  • 单向加密:也就是不可逆的加密,例如 MD5,SHA,HMAC
  • 对称加密:也就是加密方和解密方利用同一个秘钥对数据进行加密和解密,例如 DES,PBE 等等
  • 非对称加密:非对称加密分为公钥和秘钥,二者是非对称的,例如用私钥加密的内容须要应用公钥来解密,应用公钥加密的内容须要用私钥来解密,DSA,RSA

2 秘钥生成

对称加密密钥的生成

  • KeyGenerator 用于生成对称秘钥(可逆加密),或者一个明码性秘钥
  • 反对算法: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 {SecretKey secretKey = generatorDesKey();
    System.out.println(secretKey);
}
public static SecretKey generatorDesKey() throws NoSuchAlgorithmException {KeyGenerator keyGen = KeyGenerator.getInstance("DES");
    SecureRandom random = new SecureRandom();
    random.nextBytes(new byte[128]);
    keyGen.init(56,random);
    SecretKey key = keyGen.generateKey();
    return key;
}
------------ 输入后果 ------------------
com.sun.crypto.provider.DESKey@185c3

非对称加密秘钥的生成

  • KeyPairGenerator 用于生成非对称加密算法的密钥对 KeyPair,KeyPair 会包含一个公钥和私钥
  • 反对算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC
//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 {KeyPair keyPair = generatorRsaKey();
    System.out.println(keyPair);
}
public static KeyPair generatorRsaKey() throws Exception {KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
    SecureRandom random = new SecureRandom();
    random.nextBytes(new byte[516]);
    keyGen.initialize(516,random);
    KeyPair keyPair = keyGen.genKeyPair();
    System.out.println(keyPair.getPrivate());
    System.out.println(keyPair.getPublic());
    return keyPair;
}
  • 输入后果
SunRsaSign RSA private CRT key, 516 bits
  params: null
  modulus: 126519853979546358862851378153247782379894323767375778571361894186790679401365500006956495592162216057219204240578435837612184688685910973224797092901015673
  private exponent: 84346569319697572575234252102165188253262882511583852380907929457860452934243188047935652497010382336410866699832067872276413297543254894848799721123249067
Sun RSA public key, 516 bits
  params: null
  modulus: 126519853979546358862851378153247782379894323767375778571361894186790679401365500006956495592162216057219204240578435837612184688685910973224797092901015673
  public exponent: 3
java.security.KeyPair@5010be6

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

If the key is stored on a hardware device, its specification may contain information that helps identify the key on the device

KeySpec 是一个接口,用来组成加密密钥的密钥内容的(通明)标准。如果密钥存储在硬件设施上,则其标准能够蕴含有助于标识该设施上的密钥的信息

  • KeySpec 具备规范性,所以个别会依据内部参数生成 KeySpec,再依据 KeySpec 生成对应的 Key(集体了解,如有浅见,请说出你的见解)。SecretKeyFactory、KeyFactory 的作用就是转换 Key 与 KeySpec

SecretKeyFactory:用于对称加密的密钥和密钥规格之间的转换,配合 KeyGenerator 应用

  • 反对算法:AES、ARCFOUR、DES、DESede、PBEWithMD5AndDES、PBEWithHmacSHA256AndAES_128、PBKDF2WithHmacSHA256
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 = "helloWWW".getBytes(StandardCharsets.UTF_8);// 设置密钥
    DESKeySpec keySpec = new DESKeySpec(DESKey);// 设置密钥参数
    SecretKey key = keyFactory.generateSecret(keySpec);// 失去密钥对象
    System.out.println(key);
}
------------ 输入后果 ------------------
com.sun.crypto.provider.DESKey@18e49

KeyFactory:用于非对称加密的密钥和密钥规格之间的转换,配合 KeyPairGenerator 应用

  • 反对算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC
//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 秘钥对;generatorRsaKey 是下面示例提供的函数
    KeyPair keyPair = generatorRsaKey();
    System.out.println(keyPair);
    //PublicKey 转 KeySpec;KeySpec 再转 PublicKey
    X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
    System.out.println(pubKey);
    //PrivateKey 转 KeySpec;KeySpec 再转 PrivateKey
    PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
    PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
    System.out.println(priKey);
}
  • 输入后果
java.security.KeyPair@78e03bb5
Sun RSA public key, 1024 bits
  params: null
  modulus: 94134923375030889337699664145116176095803777687781162111756914700229869014912695784710407302811615186395818803402552376808400599961548587586207216709744471870318354813036696801675648731428269930963470277811176883827680414539855481218813862408748594430021606927061565116386180650249935749556615770533203721821
  public exponent: 65537
SunRsaSign RSA private CRT key, 1024 bits
  params: null
  modulus: 94134923375030889337699664145116176095803777687781162111756914700229869014912695784710407302811615186395818803402552376808400599961548587586207216709744471870318354813036696801675648731428269930963470277811176883827680414539855481218813862408748594430021606927061565116386180650249935749556615770533203721821
  private exponent: 67868152791098303572124282937222322055125020915630253288684471666171190487123683962152169691286583419399765605089805755591451063493647416931630849589322449230367252892862038338916192807582203337302166911147185956153147905653905702289234855039234840869874793012808454810161546053566242403672442319692325665473

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

  • 单向加密是不可逆的,MD5、SHA、MAC 都是属于单向加密算法的一种,也称之为摘要算法
  • MD5、SHA 它们会依据明文用哈希算法计算一个固定长度的摘要(哈希值),而后把明文和摘要发送给接收者,接收者依据同样的算法计算出摘要,比照两个摘要是否一样即可验证明文的正确性,它的利用场景是:避免篡改和校验数据
  • MD5、SHA 等算法是开源的,容易被试探进去。有没有更平安的摘要算法呢?HMAC- 带密钥 (明码) 的 hash 函数,用一个密钥和一个明文音讯作为输出,生成一个音讯摘要。密钥个别应用 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
  • MD5 的示例
MessageDigest digest = MessageDigest.getInstance("MD5");
System.out.println(new String(digest.digest("hello world!".getBytes())));
System.out.println(new String(digest.digest("hello world!".getBytes())));
------------ 输入后果 ------------------
0���G?�w
0���G?�w
  • 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());
    byte[] result2 = mac.doFinal(data.getBytes());
    System.out.println(new String(result1).equals(new String(result2)));
}
------------ 输入后果 ------------------    
true

4 签名算法工具 -Signature

  • 签名算法其实也是加密算法,它加密后的数据具备惟一标识性,就像一个人的签名能代表一个人身份。签名个别是指用非对称加密算法的私钥来加密明文的过程,生成的密文能够被持有公钥的人辨认解密,只有你的公钥是精确对应无误的,就能保障你解密的数据是来自持有私钥的一方
  • 如何保障公钥是正确无误,没被篡改的?1: 一对一给你,2: 获取公钥后通过权威机构认证,相干过程能够看下之前写的一篇文章网络篇:敌人面试之 https 认证加密过程
  • 反对算法:NONEwithRSA、MD2withRSA、MD5withRSA、SHA512/224withRSA、SHA512/256withRSA、RSASSA-PSS、NONEwithDSA、SHA512withDSA、NONEwithECDSA、SHA512withECDSA、MD5withRSAandMGF1(太多了,抉择列举几个)
  • Signature.API 示例,配合 KeyPairGenerator 应用
public static void main(String[] args) throws Exception {KeyPair keyPair = generatorRsaKey();
    Signature signature = Signature.getInstance("MD5withRSA");
    signature.initSign(keyPair.getPrivate());
    // 加解密数据
    byte[] data = "hello world".getBytes();
    // 数据签名
    signature.update(data);
    byte[] digest = signature.sign();
    // 数据解密加验证
    signature.initVerify(keyPair.getPublic());
    signature.update(data);
    System.out.println("验证后果:"+signature.verify(digest));
}
------------ 输入后果 ------------------
验证后果:true

5 罕用加密工具类 -Cipher

  • 用于加密 / 解密数据。反对各种类型的算法:对称加密(例如 AES),非对称加密(例如 RSA)
  • 反对算法:AES、AESWrap、ARCFOUR、Blowfish、DES、DESede、DESedeWrap、ECIES、RSA(太多了,抉择列举几个)
  • 示例
public static void main(String[] args) throws Exception {KeyPair keyPair = generatorRsaKey();
    Cipher cipher = Cipher.getInstance("RSA");
    // 编码前设定编码方式及密钥
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
    // 加解密数据
    byte[] data = "hello world".getBytes();
    // 数据签名
    byte[] enData = cipher.doFinal(data);
    // 数据解密
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPublic());
    byte[] newData = cipher.doFinal(enData);
    System.out.println("验证后果:"+new String(newData));
}
------------ 输入后果 ------------------
验证后果:hello world

6 Certificate- 证书存储

  • CertificateFactory:用于创立公钥证书 (Certificate) 和证书撤消列表(CRL)
  • Certificate 及其子类 X509Certificate
  • CertPath 和 CertPathBuilder:用于构建证书链(也称为证书门路)
  • CertPathValidator:用于验证证书链
  • CRL:证书撤消列表
  • CertStore:用于存储检索证书和 CRL
  • CertificateFactory 和 Certificate 的示例
  • 示例
//certificateStream 是证书的输出流
public static PublicKey getPublicKeyByCer(InputStream certificateStream) throws Exception{CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
    Certificate certificate = certificateFactory.generateCertificate(certificateStream);
    return certificate.getPublicKey();}

7 KeyStore- 密钥证书的实体类

  • KeyStore 用于存储私钥和证书(公钥在证书 Certificate 外面)
  • 公钥:是一个具体的实体的数字关联, 并无意让所有想同这个实体产生信赖关系的其余实体晓得. 公共钥匙用来测验签名;
  • 私钥:是一些数字, 公有和公共钥匙存在所有用公共钥匙加密的零碎的钥匙对中. 公共钥匙用来加密数据,公有钥匙用来计算签名. 公钥加密的音讯只能用私钥解密,私钥签名的音讯只能用公钥测验签名。
  • 示例
public static void main(String[] args) throws Exception {
    InputStream certificateStream = null;
    // 依据 Certificate 生成 KeyStore
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    keyStore.load(null);
    keyStore.setCertificateEntry("certificate", certificateFactory.generateCertificate(certificateStream));
    // 加载 jks 文件,并生成 KeyStore
    KeyStore trustKeyStore = KeyStore.getInstance("jks");
    FileInputStream trustKeyStoreFile = new FileInputStream("/root/trustKeyStore.jks");
    trustKeyStore.load(trustKeyStoreFile, "password".toCharArray());
}

8 java.https 加载证书的 API

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

个别的证书加载过程

  • 用 Certificate、KeyStore 生成创立 KeyManagerFactory 和 TrustManagerFactory
  • KeyManagerFactory 和 TrustManagerFactory 用来创立 KeyManager 和 TrustManager
  • 而 KeyManager 和 TrustManager 用来初始化 SSLContext
  • 而后应用 SSLContext,创立理论实现 SSL/TLS 协定的对象(SSLSocketFactory、SSLSocket 或者 SSLEngine)
  • SSLSocket 和 SSLEngine 能够间接在通信对象中应用
  • KeyManager 和 TrustManager 作用:

    • KeyManager 负责向对等端显示应用的凭证(应用的明码规范、加密算法、证书、公钥、签名等)
    • TrustManager 负责验证从对等端收到的凭证,验证凭证有多种形式:其中之一是创立 CertPath 对象,并让 JDK 的内置公钥根底构造(PKI)框架解决验证。在外部,CertPath 实现可能会创立一个 Signature 对象,并应用它来验证证书链中的每个签名
  • 示例:生成 SSLContext,并应用 SSLContext 初始化 apache-httpClient
public static String postWithSSL(String url, String jsonBody) throws Exception {SSLContext sslContext = getSslContext();
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}, null,
            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    RequestConfig config = RequestConfig.custom()
            .setConnectTimeout(3000)
            .setSocketTimeout(3000)
            .build();
    CloseableHttpClient client = HttpClients.custom()
            .setSSLSocketFactory(sslConnectionSocketFactory)
            .setDefaultRequestConfig(config).build();
    HttpPost httpPost = new HttpPost(url);
    //httpPost.setHeaders(headers);
    httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
    httpPost.setHeader("Accept", "application/json");
    httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
    HttpResponse response = client.execute(httpPost);
    HttpEntity responseEntity = response.getEntity();
    String result = EntityUtils.toString(responseEntity, "UTF-8");
    return result;
}
// 双向加密 SSLContext
private static SSLContext getSslContext() throws Exception {
    // 本身私钥
    KeyStore identityKeyStore = KeyStore.getInstance("jks");
    FileInputStream identityKeyStoreFile = new FileInputStream("/root/myServer.jks");
    identityKeyStore.load(identityKeyStoreFile, "password1".toCharArray());
    // 服务端信赖证书
    KeyStore trustKeyStore = KeyStore.getInstance("jks");
    FileInputStream trustKeyStoreFile = new FileInputStream("/root/trustKeyStore.jks");
    trustKeyStore.load(trustKeyStoreFile, "password".toCharArray());
    // 构建 SSLContexts
    return SSLContexts.custom()
            .loadKeyMaterial(identityKeyStore, "password1".toCharArray()) // load identity keystore
            .loadTrustMaterial(trustKeyStore, null) // load trust keystore
            .build();}
// 双向加密 SSLContext 形式二
private static SSLContext getSslContext2() throws Exception{
    // 本身私钥
    KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore keystore = KeyStore.getInstance("jks");
    keystore.load(new FileInputStream(new File("/root/myServer.jks")), "password".toCharArray());
    keyFactory.init(keystore, "password".toCharArray());
    KeyManager[] keyManagers = keyFactory.getKeyManagers();
    // 服务端信赖证书
    TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("SunX509");
    KeyStore tsStore = KeyStore.getInstance("jks");
    tsStore.load(new FileInputStream(new File("/root/trustKeyStore.jks")), "password".toCharArray());
    trustFactory.init(tsStore);
    TrustManager[] trustManagers = trustFactory.getTrustManagers();
    // 初始化 SSLContext
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keyManagers, trustManagers, null);
    return sslContext;
}

欢送指注释中谬误

参考文章

  • JCA-Java 加密框架
  • Java 加密框架 (JCA) 简要阐明
  • Java 加密解密之 MAC
  • 对于 keyGenerator,KeyPairGenerator,SecretKeyFactory 的解析
  • JCA 实际记录——SecretKeyFactory
  • HttpClient 双向认证
  • java 内置可用加密算法文档
  • key 解析
退出移动版