Java主流对称/非对称加密大全

1.简介

加密方法分为对称加密和非对称加密,区别在于对称加密只有有一个密钥,而非对称加密有两个密钥

  • 对称加解密:enccrypt(明文,密钥)=密文,decrypt(密文,密钥)=明文
  • 非对称加解密:encrypt(明文,公钥)=密文,decrypt(密文,私钥)=明文

2.密钥

2.1.密钥格式

2.1.1.der

.der密钥以二进制编码,在JAVA中需要以流的形式读取密钥。下面是获取公钥的例子:

     /**
     * der公钥解密,密文Base64输出
       */
       public static PublicKey getPublicKeyByDer(InputStream is) throws Exception {
       try {
           if (certificatefactory == null) {
               certificatefactory = CertificateFactory.getInstance("X.509");
           }
           Certificate cert = certificatefactory.generateCertificate(is);
           return cert.getPublicKey();
       }finally {
           if (is != null) {
               is.close();
           }
       }
       }

2.1.2.pem

.pem以"-----BEGIN..."开头, "-----END..."结尾,内容是BASE64编码。下面是获取公钥的例子:

 public static PublicKey getPublicKeyByPem(String publicKey) throws Exception {
       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
       X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
       return keyFactory.generatePublic(keySpec);
   }

2.1.3.PKCS12

.p12PKCS#12证书,简单的说,它包含证书和私钥,并允许口令保护。 下面是获取私钥的例子:

     public static Key getP12Key() throws Exception{
       try (InputStream is = new FileInputStream( "xx.p12")) {
           if (certificatefactory == null) {
               certificatefactory = CertificateFactory.getInstance("X.509");
           }
           KeyStore keystore = KeyStore.getInstance("PKCS12");
           keystore.load(is, "p12证书的密码".toCharArray());
           Key key = keystore.getKey("p12证书的别名", "p12证书的密码".toCharArray());
           key.getAlgorithm();//密钥对应的加密算法,可以用在 Cipher.getInstance(key.getAlgorithm())
           return key;
       }
   }

2.1.4.纯字符串

字符串密钥也可以是Base64编码或者16进制编码,甚至直接是字符串,想办法把密钥转换为byte[]就可以生成密钥。

RSA密钥可以这样获取:

 public static PublicKey getRSAPublicKey(byte[] publicKey) throws Exception {
       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
       X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
       return keyFactory.generatePublic(keySpec);
   }

AES密钥可以这样获取:

 public static Key getKey(byte[] key) throws Exception {
       return new SecretKeySpec(key, "AES");
   }

2.2.类型

  • X509EncodedKey

  • PKCS8EncodedKey

  • RSAPublicKey

  • RSAPrivateKey

    扫描二维码关注公众号,回复: 6364966 查看本文章
  • DSAPublicKey

  • DSAPrivateKey

2.3.长度

  • 128bit
  • 256bit JAVA对AES的密钥长度有限制,最多不能超过128bit,也就是16位字符串。如果是256bit的密钥,也就是32位字符串,可以通过修改JRE的文件或者使用CryptoJS的AES方法来加密解密。

2.4.密钥加密

  • PBKDF2WithHmacSHA1PBKDF2(Password-Based Key Derivation Function)是一个用来导出密钥的函数,常用于生成加密的密码。它的基本原理是通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。

以下是PBKDF2WithHmacSHA1的JAVA实现

   private static final String DEFAULT_ALGORITHM = "PBKDF2WithHmacSHA1";
   private static final int DEFAULT_ITERATIONS = 10000;
   private static final int DEFAULT_HASH_SIZE = 128;
   private static final byte[] DEFAULT_SALT = new byte[]{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
   
   public static byte[] hash(String key) throws Exception {
       char[] password = key.toCharArray();
       return hash(password, DEFAULT_ALGORITHM, DEFAULT_ITERATIONS, DEFAULT_HASH_SIZE, DEFAULT_SALT);
   }
 
   /**
     * @param password 密钥
     * @param algorithm 运算算法
     * @param iteration 进行重复计算的次数
     * @param hashSize 期望得到的密钥的长度
     * @param salt     盐值
     * @return 最后生成的密钥
     * @throws Exception 各种异常
     */
   public static byte[] hash(char[] password, String algorithm, int iteration, int hashSize, byte[] salt) throws Exception {
           SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
           PBEKeySpec spec = new PBEKeySpec(password, salt, iteration, hashSize);
           return skf.generateSecret(spec).getEncoded();
   }

3.加密算法

3.1.对称

3.1.1.DES

DES是对称性加密里面常见一种,全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法。密钥长度是64位(bit),超过位数密钥被忽略。所谓对称性加密,加密和解密密钥相同。对称性加密一般会按照固定长度,把待加密字符串分成块。不足一整块或者刚好最后有特殊填充字符。往往跨语言做DES加密解密,经常会出现问题。往往是填充方式不对、或者编码不一致、或者选择加密解密模式(ECB,CBC,CTR,OFB,CFB,NCFB,NOFB)没有对应上造成。常见的填充模式有: 'pkcs5','pkcs7','iso10126','ansix923','zero' 类型,包括DES-ECB,DES-CBC,DES-CTR,DES-OFB,DES-CFB

 public static byte[] desEncrypt(byte[] data, byte[] key) throws Exception {
       Cipher cipher = Cipher.getInstance("DES");
       SecretKeySpec secretKeySpec = new SecretKeySpec(key, "DES");
       cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
       return cipher.doFinal(data);
   }

3.1.2.3DES

3DES(又叫Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。密钥长度是128位,192位(bit),如果密码位数少于等于64位,加密结果与DES相同。原版DES容易被破解,新的3DES出现,增加了加密安全性,避免被暴力破解。它同样是对称性加密,同样涉及到加密编码方式,及填充方式。包括3DES-ECB,3DES-CBC,3DES-CTR,3DES-OFB,3DES-CFB

 public static byte[] desedeEncrypt(byte[] data, byte[] key) throws Exception {
       Cipher cipher = Cipher.getInstance("DESede");
       SecretKeySpec secretKeySpec = new SecretKeySpec(key, "DESede");
       cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
       return cipher.doFinal(data);
   }

3.1.3. AES

AES,高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。严格地说,AES和Rijndael加密法并不完全一样(虽然在实际应用中二者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度固定为128 比特,密钥长度则可以是128,192或256比特;而Rijndael使用的密钥和区块长度可以是32位的整数倍,以128位为下限,256比特为上限。包括AES-ECB,AES-CBC,AES-CTR,AES-OFB,AES-CFB

 public static byte[] aesEncrypt(byte[] data, byte[] key) throws Exception {
       Cipher cipher = Cipher.getInstance("AES");
       SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
       cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
       return cipher.doFinal(data);
   }

AES的算法模式和填充有如下几种选择,其中CBC需要偏移量而ECB可以不用偏移量。NoPadding为不填充,这时候需要手动保证加密或者解密的内容的字节数是16的倍数;PKCS5Padding和PKCS7Padding是几乎一样的,可以通用,填充的内容是填充字节序列的长度,例如如果byte数组的长度为15,需要填充1位才达到16,那就填充0x1,如果byte数组长度为1,则需要填充15个0xf;ZeroPadding是用0x0来填充。

Cipher.getInstance("AES/CBC/NoPadding");
Cipher.getInstance("AES/CBC/PKCS5Padding");
Cipher.getInstance("AES/CBC/PKCS7Padding");
Cipher.getInstance("AES/CBC/ZeroBytePadding");
Cipher.getInstance("AES/ECB/NoPadding");
Cipher.getInstance("AES/ECB/PKCS5Padding");
Cipher.getInstance("AES/ECB/PKCS7Padding");
Cipher.getInstance("AES/ECB/ZeroBytePadding");

另外,AES的密钥长度分别是128byte、192byte、256byte,对应的字符串长度分别是16位、24位、32位。当密钥大于128byte,也就是16位字符串长度时,代码会抛出java.security.InvalidKeyException: Illegal key size or default parameters。Illegal key size or default parameters是指密钥长度是受限制的,java运行时环境读到的是受限的policy文件。文件位于${java_home}/jre/lib/security,这种限制是因为美国对软件出口的控制。解决方法有两个

- 替换${java_home}/jre/lib/security/ 下面的local_policy.jar和US_export_policy.jar。
- 使用CryptoJs,这是谷歌开源的用Js实现的AES加解密库,速度和原生JDK相差无几,除了第一次载入Js文件需要额外耗时。

3.1.4.RC4

RC4加密算法是RSA三人组中的头号人物Ron Rivest在1987年设计的密钥长度可变的流加密算法簇。该算法的速度可以达到DES加密的10倍左右,且具有很高级别的非线性。1994年9月,它的算法被发布在互联网上。由于RC4算法加密是采用的xor,所以,一旦子密钥序列出现了重复,密文就有可能被破解。RC4作为一种老旧的验证和加密算法易于受到黑客攻击,现在逐渐不推荐使用了。

 public static byte[] rc4Encrypt(byte[] data, byte[] key) throws Exception {
       Cipher cipher = Cipher.getInstance("RC4");
       SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ARCFOUR");
       cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
       return cipher.doFinal(data);
   }

3.2.非对称

3.2.1. RSA

加解密密钥不一致,一般私钥不公开,使用公钥加密,私钥解密,使用私钥加密,公钥可以解密。与AES不同,每次加密出来的结果是不一样的。而且需要执行分组加密,RSA加密每一组最多117个byte的长度,解密每一组最多128个byte的长度。

public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
       Cipher cipher = Cipher.getInstance("RSA");
       KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
       X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
       PublicKey key = keyFactory.generatePublic(keySpec);
       cipher.init(Cipher.ENCRYPT_MODE, key);
       //执行分组加密操作,RSA加密每一组最多117位长度
       int segment = 117;
       int inputLen = data.length;
       try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
           int offSet = 0;
           byte[] cache;
           int i = 0;
           // 对数据分段加密
           while (inputLen - offSet > 0) {
               if (inputLen - offSet > segment) {
                   cache = cipher.doFinal(data, offSet, segment);
               } else {
                   cache = cipher.doFinal(data, offSet, inputLen - offSet);
               }
               out.write(cache, 0, cache.length);
               i++;
               offSet = i * segment;
           }
           return out.toByteArray();
       }
   }
 
 public static byte[] encryptByPrivateKey(byte[] data, byte[] publicKey) throws Exception {
       Cipher cipher = Cipher.getInstance("RSA");
       KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
       X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
       PrivateKey key = keyFactory.generatePrivate(keySpec);
       cipher.init(Cipher.ENCRYPT_MODE, key);
       //执行分组加密操作,RSA加密每一组最多117位长度
       int segment = 117;
       int inputLen = data.length;
       try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
           int offSet = 0;
           byte[] cache;
           int i = 0;
           // 对数据分段加密
           while (inputLen - offSet > 0) {
               if (inputLen - offSet > segment) {
                   cache = cipher.doFinal(data, offSet, segment);
               } else {
                   cache = cipher.doFinal(data, offSet, inputLen - offSet);
               }
               out.write(cache, 0, cache.length);
               i++;
               offSet = i * segment;
           }
           return out.toByteArray();
       }
   }

3.3.补充额外算法

添加BouncyCastle的依赖,在程序初始化时调用一次

 Security.addProvider(new BouncyCastleProvider());

就可在Cipher.getInstance("")方法中获取更多加密算法。

4.签名

4.1.算法

4.1.1.SHA1withRSA

   /**
     * RSA签名
     *
     * @param content   待签名数据
     * @param privateKey 商户私钥
     * @param encode     字符集编码
     * @return 使用Base64编码的签名值
     */
   public static String sign(String content, String privateKey, String encode) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
       PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
 
       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
       PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
 
       java.security.Signature signature = java.security.Signature.getInstance("SHA256withRSA");
 
       signature.initSign(priKey);
       signature.update(content.getBytes());
 
       byte[] signed = signature.sign();
       return Base64.encode(signed);
   }

4.1.2. 其余通用私钥签名

/**
     * 私钥签名底层操作
     *
     * @param algorithm NONEwithRSA
     *                   MD2withRSA,MD5withRSA
     *                   SHA1withRSA,SHA256withRSA ,SHA384withRSA ,SHA512withRSA
     *                   NONEwithDSA
     *                   SHA1withDSA
     *                   NONEwithECDSA ,SHA1withECDSA ,SHA256withECDSA ,SHA384withECDSA ,SHA512withECDSA
     * @param data       byte[]
     * @param privateKey PrivateKey
     * @return byte[]
     */
   public static byte[] sign(String algorithm, byte[] data, PrivateKey privateKey) throws Exception {
       Signature signature = Signature.getInstance(algorithm);
       signature.initSign(privateKey);
       signature.update(data);
       return signature.sign();
   }

5.散列

  • SHA1
  • SHA256
  • SHA384
  • SHA512
  • MD2
  • MD5

apache-commons-codec的DigestUtils有全部实现。

参考资料

https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html Java官方实现的各种加密算法名称的文档

https://www.example-code.com/java/rsa.asp RSA例子

https://www.programcreek.com/java-api-examples/ 查找各种算法和密钥的demo

http://tool.chacuo.net/cryptaes 校验加密算法正确性的工具网站

猜你喜欢

转载自www.cnblogs.com/datartvinci/p/10985577.html
今日推荐