廖雪峰Java10加密与安全-4加密算法-2口令加密算法

对称加密key是一个byte数组,如AES256算法的key是一个32字节的数组,普通的加密软件由用户输入加密口令。如果由用户输入口令,进行加密/解密,需要用到PBE算法。

1.PBE:Password Based Encryption

  • 由用户输入口令,采用随机数杂凑计算出密钥再进行加密
  • Password:用户口令,例如“hello123”
  • Salt:随机生成的byte[]
  • Key:由随机的salt和password计算而成:generate(byte[] salt,String password)

2.AES和PBE的对比:

AES

    byte[] message = ...;
    byte[] key = generated16Bytes();//算法随机生成16字节的数组
    byte[] encrypted = aes128_encrypt(key, message);

PBE

    byte[] message = ...;
    String password = "hello123";
    byte[] salt = random16Bytes();//随机产生一个16字节的salt
    //为什么引入salt?因为用户输入的口令通常都很短,引入一个随机的salt,既可增加口令的长度,还可以让相同的口令生成不同的key,从而提高安全性。
    byte[] key = generated16BytesFrom(password, salt); 
    byte[] encrypted = aes128_encrypt(key,message);

3.代码示例:salt不固定

package com.testList;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;

public class SplitString {
    static final String CIPHER_NAME = "PBEwithSHA1and128bitAES-CBC-BC";
    //加密
    public static byte[] encrypt(String password, byte[] salt, byte[] input) throws GeneralSecurityException {
        //传入char数组,获取一个PBEKeySpec的对象keySpec
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        //获取SecretKeyFactory对象skeyFactory
        SecretKeyFactory skeyFactory = SecretKeyFactory.getInstance(CIPHER_NAME);
        //传入keySpec,获取密钥:SecretKey类型的示例skey
        SecretKey skey = skeyFactory.generateSecret(keySpec);
        //将salt和用户输入的口令做1000次循环,获取PBEParameterSpec类型示例pbesp
        PBEParameterSpec pbeps = new PBEParameterSpec(salt, 1000);
        Cipher cipher = Cipher.getInstance(CIPHER_NAME);
        //初始化:指定加密模式,传入skey,pbeps对象
        cipher.init(Cipher.ENCRYPT_MODE, skey, pbeps);
        return cipher.doFinal(input);
    }

    public static byte[] decrypt(String password, byte[] salt, byte[] input) throws GeneralSecurityException {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        SecretKeyFactory skeyFactory = SecretKeyFactory.getInstance(CIPHER_NAME);
        SecretKey skey = skeyFactory.generateSecret(keySpec);
        PBEParameterSpec pbeps = new PBEParameterSpec(salt, 1000);
        Cipher cipher = Cipher.getInstance(CIPHER_NAME);
        //初始化:指定解密模式,传入skey,pbeps对象
        cipher.init(Cipher.DECRYPT_MODE, skey, pbeps);
        return cipher.doFinal(input);
    }

    public static void main(String[] args) throws Exception{
        //把BouncyCastle作为provider添加到java.security
        Security.addProvider(new BouncyCastleProvider());
        String message = "Hello world!encrypted using PBE!";
        String password = "hello12345";
        //16字节的salt
        byte[] salt = SecureRandom.getInstanceStrong().generateSeed(16);
        System.out.printf("salt:%032x\n",new BigInteger(1,salt));
        byte[] data = message.getBytes(StandardCharsets.UTF_8);
        byte[] encryted = encrypt(password,salt,data);
        //打印Base64加密后的密文
        System.out.println("encrypted:"+Base64.getEncoder().encodeToString(encryted));
        byte[] decrypted = decrypt(password,salt,encryted);
        System.out.println("decrypted:"+new String(decrypted,"UTF-8"));
    }
}
由于每次的salt不一样,每次的密文也不一样。

4.salt固定

如果把salt固定,就得到了一个通用的口令加密软件。
只有同事破解了salt+口令,才能解密。而salt为128为随机数,很难被破解

5.总结:

  • PBE算法通过用户口令和随机salt计算key然后再加密
  • key通过口令和随机salt计算出,提高了安全性
  • PBE算法内部使用的仍然是标准对称加密算法(例如AES)
posted on 2019-05-13 21:19  singleSpace  阅读(581)  评论(0编辑  收藏  举报