前言

和前端进行数据交互时或者和第三方商家对接时,须要对隐衷数据进行加密。单向加密,对称加密,非对称加密,其对应的算法也各式各样。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.javapublic 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.javapublic 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: 84346569319697572575234252102165188253262882511583852380907929457860452934243188047935652497010382336410866699832067872276413297543254894848799721123249067Sun RSA public key, 516 bits  params: null  modulus: 126519853979546358862851378153247782379894323767375778571361894186790679401365500006956495592162216057219204240578435837612184688685910973224797092901015673  public exponent: 3java.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.javapublic 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@78e03bb5Sun RSA public key, 1024 bits  params: null  modulus: 94134923375030889337699664145116176095803777687781162111756914700229869014912695784710407302811615186395818803402552376808400599961548587586207216709744471870318354813036696801675648731428269930963470277811176883827680414539855481218813862408748594430021606927061565116386180650249935749556615770533203721821  public exponent: 65537SunRsaSign 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?�w0���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;}//双向加密 SSLContextprivate 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解析