常用加密算法的接口介绍和JavaAPI实现

前言

据记载,公元前400年,古希腊人发明了置换密码。1881年世界上的第一个电话保密专利出现。在第二次世界大战期间,德国军方启用“恩尼格玛”密码机,密码学在战争中起着非常重要的作用。

随着信息化和数字化社会的发展,人们对信息安全和保密的重要性认识不断提高,于是在1997年,美国国家标准局公布实施了“美国数据加密标准(DES)”,民间力量开始全面介入密码学的研究和应用中,采用的加密算法有DES、RSA、SHA等。

如果你是Java开发工程师,那么你或多或少了解使用Java加解密,加签验签的api,这些也是一个Java工作者必备的技能之一。让我们一起来讨论下常用加解密技术。

背景

在日常工作中,最近对接银行需求比较多,因此对报文的加解密是一件必须做的事情,但是对接不同银行所用的加解密标准或者是使用算法不同,因此我不得不去尽可能了解更多的加解密算法和其中java API,现在我把这些了解的东西分享给大家,一起丰富我们的知识储备。

 

java加解密介绍

1.加密和解密

    1.1. 加密

    数据加密的基本过程,就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为 “密文”。通过这样的途径,来达到保护数据不被非法人窃取、阅读的目的。

    1.2. 解密

    加密的逆过程为解密,即将该编码信息转化为其原来数据的过程。

    1.3 算法分类

根据加密结果是否可以被解密,算法可以分为可逆加密和不可逆加密(单向加密),从这个意义上来说,单向加密只能称之为加密算法而不是加解密算法。对于可逆加密,又可以根据密钥的的对称性分为对称加密和非对称加密。具体的分类结构如下:

  • 可逆加密
  • 对称加密:DES,3DES,AES,PBE
  • 非对称加密:RSA,DSA,ECC
  • 不可逆加密(单向加密):MD5,SHA,HMAC

2.模式介绍

分组密码(block cipher)是每次只能处理特定长度的一块数据的一类密码算法,这里的“一块”就称为分组(block)。此外,一个分组的比特数就称为分组长度(block length)。例如,DES和三重DES的分组长度都是64比特。这些密码算法一次只能加密64比特的明文,并生成64比特的密文。AES的分组长度可以从128比特、192比特和256比特中进行选择。当选择128比特的分组长度时,AES一次可加密128比特的明文,并生成128比特的密文。

流密码(stream cipher)是对数据流进行连续处理的一类密码算法。流密码中一般以1比特、8比特或32比特等为单位进行加密和解密。分组密码处理完一个分组就结束了,因此不需要通过内部状态来记录加密的进度;相对地,流密码是对一串数据流进行连续处理,因此需要保存内部状态。

对于可逆加密,在加密时可以选择加密模式,可以理解为加密算法可以有不同的工作方式,不同的工作方式之间存在效率、方式等方面的区别。要注意的是,对同一个数据,加密选择的模式与解密选择的模式必须相同,否则解密得不到正确的结果。主要的加密模式ECB (电子密码本模式)、CBC(分组连接模式)、CFB(密码反馈模式)、OFB (输出反馈模式)  CTR计算器模式

  • 什么是模式

2.1 ECB (电子密码本模式):

其使用方式是一个明文分组加密成一个密文分组,相同的明文分组永远被加密成相同的密文分组。直接利用加密算法分别对每个64位明文分组使用相同的64位密钥进行加密。每个明文分组的处理是相互独立的。这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。

缺点:在给定密钥k 下,同一明文组总是产生同一密文组,这会暴露明文组的数据格式。某些明文的数据格式会使得明文组有大量的重复或较长的零串,一些重要的数据常常会在同一位置出现,特别是格式化的报头、作业号、发报时间、地点等特征都将被泄露到密文之中,使攻击者可以利用这些特征。

优点:用同个密钥加密的单独消息,其结果是没有错误传播。实际上,每一个分组可被看作是用同一个密钥加密的单独消息。密文中数据出了错,解密时,会使得相对应的整个明文分组解密错误,但它不会影响其他明文。然而,如果密文中偶尔丢失或添加一些数据位,那么整个密文序列将不能正确的解密。除非有某帧结构能够重新排列分组的边界。

2.2 CBC(分组连接模式):

对于相同的明文,加密结果不同。这就加大了密码破解者的破译难度。在密钥固定不变的情况下,改变每个明文组输入的链接技术,这样, 密文组不仅与当前的明文组有关,而且通过反馈的作用还与以前的明文组有关。这从密码学的本质上来说是一种混淆操作。这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

优点:能隐蔽明文的数据模式; 在某种程度上能防止数据篡改, 诸如明文组的重放,嵌入和删除等.

缺点:会出现错误传播(errorpropagation). 密文中任一位发生变化会涉及后面一些密文组. 但CBC 模式的错误传播不大, 一个传输错误至多影响两个消息组的接收结果,错误传播最多持续2个分组

3.密钥介绍

密钥在加解密算法中是一个参数,其长度根据不同的算法有所不同,同一算法的密钥长度也有可能有不同的要求。一般来说,密钥的长度与安全性成正比。在使用时,将明文(或密文)连同密钥放入相应的加密(或解密容器),即可得到密文(或明文),实现加解密。

keyGenerator:秘钥生成器,也就是更具算法类型随机生成一个秘钥,例如HMAC,所以这个大部分用在非可逆的算法中KeyFactory:秘密秘钥工厂,言外之意就是需要根据一个秘密(password)去生成一个秘钥,例如DES,PBE,所以大部分使用在对称加密中KeyPairGenerator:秘钥对生成器,也就是可以生成一对秘钥,也就是公钥和私钥,所以大部分使用在非对称加密中

1.keyGenerator
public static byte[] generateKey(int keySize) throws Exception {
    KeyGenerator kg = KeyGenerator.getInstance(ALGORIGTHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
    kg.init(keySize, new SecureRandom());
    return kg.generateKey().getEncoded();
}
2.KeyFactory
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
3.KeyPairGenerator
类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024,new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥

4.填充方式介绍

ZeroPadding,数据长度不对齐时使用0填充,否则不填充。

PKCS7Padding,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。

PKCS5Padding,PKCS7Padding的子集,块大小固定为8字节。

5.算法介绍

 1.单向加密

前面说过,单向加密的结果是不可以被解密的,因此,单向加密的主要用途并不是传统意义上的加解密工作,而是对明文数据的保密和摘要提取。单向加密主要有MD5、SHA、HMAC等算法。

    MD5:
  MD5是应用最广泛的一种单向加密算法,其在数据加密、安全访问认证和文件完整性验证等方面都有应用。MD5加密输出是一个128位的十六进制数字串。

    SHA:
  SHA实际上是一组加密算法的合称,包括SHA-1,SHA-256,SHA-384,SHA-512。其中应用最广的是SHA-1,HTTPS中使用的HASH散列函数多使用SHA-1。相比较于MD5,SHA族有更高的安全性,到目前为止还没有人能破译其加密结果,但其加密速度比MD5慢,是以速度换取了安全性

/**
 *
 * @param strSrc 需要被加密的字符串
 * @param encName 加密方式,有 MD5、SHA-1和SHA-256 这三种加密方式
 * @return 返回加密后的字符串
 */
private static String EncryptStr(String strSrc, String encName) {
    MessageDigest md = null;
    String strDes = null;

    byte[] bt = strSrc.getBytes();
    try {
        if (StringUtils.isBlank(encName)) {
            encName = "MD5";
        }
        md = MessageDigest.getInstance(encName);
        md.update(bt);
        strDes = bytes2Hex(md.digest()); // to HexString
    } catch (NoSuchAlgorithmException e) {
        System.out.println("Invalid algorithm.");
        return null;
    }
    return strDes;
}

2.对称加密

对称加密算法,应用的时间比较早,技术相对来说比较成熟,在对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。对称加密算法的特点是算法公开、计算量小。不足之处是,交易双方都使用同样钥匙,安全性得不到保证。

  • 特点

    1. 密钥较小(一般小于256bit),密钥越大,加密越强,但加密解密越慢
    2. 优点:算法公开、计算量小、加密速度快、加密效率高,适用于大量数据的加密
    3. 缺点:密钥分配与管理,安全性较低
    4. 四种算法DES,3DES,AES,PBE

1.DES:
  DES算法是一种分组加密机制,将明文分成N个组,然后对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位。

/**
 * 加密
 * @param datasource byte[]
 * @param password String
 * @return byte[]
 */
public static  byte[] encrypt(byte[] datasource, String password) {
    try{
        SecureRandom random = new SecureRandom();
        DESKeySpec desKey = new DESKeySpec(password.getBytes());
        //创建一个密匙工厂,然后用它把DESKeySpec转换成密钥
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(desKey);
        //Cipher对象实际完成加密操作
        Cipher cipher = Cipher.getInstance("DES");
        //用密匙初始化Cipher对象
        cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
        //现在,获取数据并加密
        //正式执行加密操作
        return cipher.doFinal(datasource);
    }catch(Throwable e){
        e.printStackTrace();
    }
    return null;
}
/**
 * 解密
 * @param src byte[]
 * @param password String
 * @return byte[]
 * @throws Exception
 */
public static byte[] decrypt(byte[] src, String password) throws Exception {
    // DES算法要求有一个可信任的随机数源
    SecureRandom random = new SecureRandom();
    // 创建一个DESKeySpec对象
    DESKeySpec desKey = new DESKeySpec(password.getBytes());
    // 创建一个密匙工厂
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
    // 将DESKeySpec对象转换成SecretKey对象
    SecretKey securekey = keyFactory.generateSecret(desKey);
    // Cipher对象实际完成解密操作
    Cipher cipher = Cipher.getInstance("DES");
    // 用密匙初始化Cipher对象
    cipher.init(Cipher.DECRYPT_MODE, securekey, random);
    // 真正开始解密操作
    return cipher.doFinal(src);
}

2.3DES:
  3DES算法是在DES的基础上发展出来的。在一些对安全性要求较高的场景下,DES的64位密钥安全性不能满足要求,于是人们采取了一种“简单暴力”的办法——三重数据加密,对数据进行加密,这样来说,破解的概率就小了很多。3DES的密钥长度为168位。由于3DES与DES的使用极其相似,只是密钥的长度有所改变,这里就不展开介绍了,感兴趣的读者可以尝试使用相应接口。

/**
 * DESede 加密操作
 *
 * @param content
 *            待加密内容
 * @param key
 *            加密密钥
 * @return 返回Base64转码后的加密数据
 */
public static String encrypt(String content, String key) {
    try {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        // 创建密码器
        byte[] byteContent = content.getBytes("utf-8");
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
        // 初始化为加密模式的密码器
        byte[] result = cipher.doFinal(byteContent);// 加密
        return Base64.encodeBase64String(result);// 通过Base64转码返回
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

/**
 * } } DESede 解密操作
 *
 * @param content
 * @param key
 * @return
 */
public static String decrypt(String content, String key) {
    try {
        // 实例化
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); // 使用密钥初始化,设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key)); // 执行操作
        byte[] result = cipher.doFinal(Base64.decodeBase64(content));
        return new String(result, "utf-8");
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

3.AES:
  AES 加密算法作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。AES 设计有三个密钥长度:128,192,256 位。是目前可获得的最安全的加密算法。

/**
 * @param content 加密前原内容
 * @param pkey    长度为16个字符,128位
 * @return base64EncodeStr   aes加密完成后内容
 * @throws
 * @Title: aesEncryptStr
 * @Description: aes对称加密
 */
public static String aesEncryptStr(String content, String pkey) throws UnsupportedEncodingException {

    content=Base64.encodeBase64String(content.getBytes("UTF-8"));
    byte[] aesEncrypt = aesEncrypt(content, pkey);
    System.out.println("加密后的byte数组:" + Arrays.toString(aesEncrypt));
    String base64EncodeStr = Base64.encodeBase64String(aesEncrypt);
    System.out.println("加密后 base64EncodeStr:" + base64EncodeStr);
    base64EncodeStr= URLEncoder.encode(base64EncodeStr,"UTF-8");
    return base64EncodeStr;
}

/**
 * @param content base64处理过的字符串
 * @param pkey    密匙
 * @return String    返回类型
 * @throws Exception
 * @throws
 * @Title: aesDecodeStr
 * @Description: 解密 失败将返回NULL
 */
public static String aesDecodeStr(String content, String pkey) throws Exception {
    try {
        content=URLDecoder.decode(content,"UTF-8");
        System.out.println("待解密内容:" + content);
        byte[] base64DecodeStr = Base64.decodeBase64(content);
        System.out.println("base64DecodeStr:" + Arrays.toString(base64DecodeStr));
        byte[] aesDecode = aesDecode(base64DecodeStr, pkey);
        System.out.println("aesDecode:" + Arrays.toString(aesDecode));
        if (aesDecode == null) {
            return null;
        }
        String result;
        result = new String(Base64.decodeBase64(aesDecode), "UTF-8");
        System.out.println("aesDecode result:" + result);
        return result;
    } catch (Exception e) {
        System.out.println("Exception:" + e.getMessage());
        throw new Exception("解密异常");
    }
}

 

3.非对称加密

非对称加密算法需要两个密钥来进行加密和解密,分别是公钥和私钥,需要注意的一点,这个公钥和私钥必须是一对的,如果用公钥对数据进行加密,那么只有使用对应的私钥才能解密,反之亦然。非对称加密算法的出现,就是为了解决只有一把密钥的加解密,只要这一把密钥丢失或者被公开,那么加密数据就很容易被攻击。同时,也正是由于非对称加密算法的出现,才有了后面的数字签名、数字证书等等。

  • 特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。

  • 使用:非对称加密主要有两种使用方面:加解密和数字签名验证。

RSA

  • 优点:
      密码分配简单,安全保障性高

  • 缺点:

      1.速度慢,RSA最快的情况也比DES慢上好几倍,RSA的速度比对应同样安全级别的对称密码算法要慢1000倍左右
  2.一般来说只用于少量数据加密
  3.产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。实际上,这些缺点是非对称加密本身的局限。

/**
 * RSA公钥加密
 *
 * @param str
 *            加密字符串
 * @param publicKey
 *            公钥
 * @return 密文
 * @throws Exception
 *             加密过程中的异常信息
 */
public static String encrypt( String str, String publicKey,KeyPair keyPair) throws Exception{
    //base64编码的公钥
    byte[] decoded = Base64.decodeBase64(publicKey);
    //RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
    //RSA加密
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
    return outStr;
}

/**
 * RSA私钥解密
 *
 * @param str
 *            加密字符串
 * @param privateKey
 *            私钥
 * @return 铭文
 * @throws Exception
 *             解密过程中的异常信息
 */
public static String decrypt(String str, String privateKey,KeyPair keyPair) throws Exception{
    //64位解码加密后的字符串
    byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
    //base64编码的私钥
    byte[] decoded = Base64.decodeBase64(privateKey);
    RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
    //RSA解密
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
    String outStr = new String(cipher.doFinal(inputByte));
    return outStr;
}

总结

    以上是对工作中遇到的加解密算法的简单介绍,其实还有更为复杂和安全的加解密技术没有介绍例如ECC算法,HMAC等等,如果你对我介绍的算法有了一定的了解,这些更为复杂的算法我们用到的时候很自然的查阅资料就能了解到。不同算法有着不同的特点特性,开发者在选择使用时需要结合考虑使用场景、需求、成本等各个方面的因素。总之我们要对我们的接口数据负责,为了数据安全负责,再见,下回见。

 

参考:《java加密与解密的艺术》--梁栋

            https://www.jianshu.com/p/b3be35c1d424  博客园--苹果粉

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值