当前位置: 首页 > news >正文

java 加密算法的简单使用

简介

加密算法,就是将原本的明文,通过一系列操作变成密文。在这里介绍一些常用的加密算法。在日常开发中,接触到了一些加密算法,例如,用户的隐私信息,诸如密码、手机号等,需要加密后存储到数据库中,返回给用户时又需要进行解密,在这里对常见的加密算法做一个介绍。

常用加密算法

加密算法的分类:可以简单地分为信息摘要算法、对称加密算法、非对称加密算法。

  • 信息摘要算法:根据数据计算出一个固定长度的摘要,这个摘要无法被转换为原本的信息,这种算法可以用来确保信息没有被篡改。
  • 对称加密算法:使用一个秘钥对数据进行加密,同时使用相同的秘钥对数据解密。
  • 非对称加密算法:用户持有一对公钥和私钥,然后把公钥钥分发给别人。
    • 数据加密和解密:公钥加密数据,私钥解密数据。
    • 通信:如果两个用户互相交换公钥,它们就可以通信了。
    • 签名:私钥签名,公钥验签,确保数据来源是可信的。

信息摘要算法

MD5算法

MD5算法:信息摘要算法5,Message-Digest Alogrithm 5,是一种广泛应用的密码散列算法,于1991年被提出。MD5算法可将消息压缩成固定长度的摘要,通常是16字节(128位),并且这个摘要无法被转换为明文。

MD5算法存在一定程度的安全隐患,因为生成的加密字符串的长度相对较短,容易受到暴力攻击

案例:

public class CipherUtil {// 算法名的枚举public enum MsgDigestName {MD5, SHA1, SHA256}// 信息摘要算法public static byte[] messageDigest(MsgDigestName name, String str) {if (name == null) {return null;}try {MessageDigest sha1 = MessageDigest.getInstance(name.name());sha1.update(str.getBytes(StandardCharsets.UTF_8));return sha1.digest();} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);}}
}

使用场景:使用MD5算法,加密用户输入的密码,然后把密码保存到数据库中,用户输入密码时,先生成MD5串,然后再去数据库中比对,这样一来,数据库中不会存储用户密码,除了用户自己,谁也不知道他的密码

SHA1算法

SHA1算法:安全哈希算法,Secure Hash Algorithm 1,和MD5一样,也是信息摘要算法。SHA1是美国国家安全局(NSA)制定的安全哈希算法,于1995年发布。SHA1根据输入数据,生成一个20字节(168位)的哈希值。SHA1算法相对于MD5更加安全,但仍然存在哈希碰撞的问题。

SHA256算法

SHA256:SHA256 是 SHA-2 家族中的一种算法,由美国国家安全局制定,于2001年发布。SHA256根据输入数据生成一个32字节(256位)的哈希值。SHA256 算法相对于MD5和SHA1更加安全,被广泛应用于比特币等场景。

SHA1算法、SHA256算法,和MD5算法,都是使用相同的API,只是入参不同。

涉及到的API

java.security.MessageDigest类

提供了消息摘要算法的功能,包括MD5、SHA-1、SHA-256算法

使用案例:

public static byte[] sha256Encode(String str) throws NoSuchAlgorithmException {// 第一步:获取MessageDigest实例,指定要使用的算法的名称MessageDigest sha1Instance = MessageDigest.getInstance("SHA-256");// 第二步:使用update方法,向实例中设置数据sha1Instance.update(str.getBytes(StandardCharsets.UTF_8));// 第三步:调用digest方法,生成加密摘要,return sha1Instance.digest();
}

对称加密算法

DES算法

DES:Data Encryption Standard,数据加密标准,是一种对称加密算法,但是在安全性、效率和灵活性上比AES略差

案例:

public class DESUtil {private static final String DES = "DES";private static final String PADDING_WAY = "DES/ECB/PKCS5Padding";// 加密public static byte[] encode(String str, String pwd) {try {// 生成密钥SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES);DESKeySpec desKeySpec = new DESKeySpec(pwd.getBytes(StandardCharsets.UTF_8));SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);// 加密Cipher cipher = Cipher.getInstance(PADDING_WAY);cipher.init(Cipher.ENCRYPT_MODE, secretKey);return cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | NoSuchPaddingException |IllegalBlockSizeException | BadPaddingException e) {throw new RuntimeException(e);}}// 解密public static String decode(byte[] bytes, String pwd) {try {// 生成密钥SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES);DESKeySpec desKeySpec = new DESKeySpec(pwd.getBytes(StandardCharsets.UTF_8));SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);// 解密Cipher cipher = Cipher.getInstance(PADDING_WAY);cipher.init(Cipher.DECRYPT_MODE, secretKey);return new String(cipher.doFinal(bytes));} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | NoSuchPaddingException |IllegalBlockSizeException | BadPaddingException e) {throw new RuntimeException(e);}}
}

在这里案例中,只要传入相同的密码,就会生成相同的密钥,可以把它们缓存起来,增加效率。

AES算法

AES算法:Advanced Encryption Standard,高级加密标准,一种广泛使用的对称加密算法。对称加密意味着加密和解密使用相同的密钥。

AES提供了多种密钥长度,包括 128 位、192 位和 256 位。

案例:

public class AESUtil {private static final String AES = "AES";private static final String SHA1PRNG = "SHA1PRNG";private static final String PADDING_MODE = "AES/ECB/PKCS5Padding";// 加密public static byte[] encode(String str, String pwd) {try {// 生成随机数SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG);secureRandom.setSeed(pwd.getBytes(StandardCharsets.UTF_8));// 初始化秘钥生成器,第一个参数指定了秘钥长度,第二个参数指定了随机数源,用于提供加密安全的随机数。// 这确保了生成的密钥是随机的,不容易被预测或重复。KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);keyGenerator.init(256, secureRandom);// 密钥SecretKey secretKey = keyGenerator.generateKey();// 设置加密算法/模式/填充方案Cipher cipher = Cipher.getInstance(PADDING_MODE);cipher.init(Cipher.ENCRYPT_MODE, secretKey);return cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException| IllegalBlockSizeException | BadPaddingException e) {throw new RuntimeException(e);}}// 解密public static String decode(byte[] bytes, String pwd) {try {// 生成随机数SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG);secureRandom.setSeed(pwd.getBytes(StandardCharsets.UTF_8));// 生成密钥KeyGenerator aesKeyGenerator = KeyGenerator.getInstance(AES);aesKeyGenerator.init(256, secureRandom);SecretKey secretKey = aesKeyGenerator.generateKey();// 解密数据Cipher cipher = Cipher.getInstance(PADDING_MODE);cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] bytes1 = cipher.doFinal(bytes);return new String(bytes1);} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException| IllegalBlockSizeException | BadPaddingException e) {throw new RuntimeException(e);}}
}

在这个工具类中,只要传入的密码相同,就会生成相同的密钥

相关API:

1、创建随机数生成器时指定的参数:SHA1PRNG,它是一种基于SHA-1算法的伪随机数生成器算法,其原理是通过SHA-1算法生成不可预测的伪随机数序列,以达到保障密码安全的目的。PRNG:pseudo random number generator,伪随机数生成器

2、创建Cipher实例时指定的参数:AES/ECB/PKCS5Padding,这个参数分为3个部分:

  • AES是加密算法
  • ECB:Electronic Codebook,电子密码本,ECB 是AES 的一个操作模式。在 ECB 模式下,每个块(通常是 128 位或 16 字节)独立地加密。这意味着,如果原始数据中存在重复的块,那么加密后的数据中也会存在相同的块。这种模式简单、易于实现,但在某些情况下可能不太安全,因为它不提供任何形式的混淆。
  • PKCS5Padding:Public Key Cryptography Standards,公钥加密标准,指定了填充方式。填充方式用于确保待加密的数据长度符合特定算法的要求。对于某些算法,数据必须达到特定长度才能被加密。PKCS5Padding 是 PKCS#5 标准定义的填充方式,它使用一个字节的填充值来达到这个长度。填充值是原始数据长度与块大小之间的差值。

涉及到的API

jce.jar:java cipher extension,java加密扩展,提供了加密的功能

javax.crypto
KeyGenerator类

用于生成秘钥,它可以生成对称秘钥,比如AES、DES、RSA等算法的秘钥。

使用案例:

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;// 学习KeyGenerator中的方法
public class Test1 {public static void main(String[] args) throws NoSuchAlgorithmException {// 获取KeyGenerator实例,它的底层是基于spi机制KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");// 初始化KeyGenerator,设置秘钥长度为128位keyGenerator.init(128);// 生成密钥,随后可以使用这个秘钥来进行加密和解密SecretKey secretKey = keyGenerator.generateKey();// 查看keyGenerator中的属性String algorithm = keyGenerator.getAlgorithm();System.out.println("algorithm = " + algorithm);  // AESProvider provider = keyGenerator.getProvider();System.out.println("provider = " + provider);    // SunJCE version 1.8}
}
SecretKey接口

代表一个秘钥,用于对称加密算法,它提供了管理密钥的方法,如生成、复制、销毁等。

代码:

public interface SecretKey extends Key, Destroyable {long serialVersionUID = -4795878709595146952L;
}

SecretKey接口的父接口:

public interface Key extends java.io.Serializable {static final long serialVersionUID = 6603384152749567654L;public String getAlgorithm();  // 密钥使用的算法public String getFormat();public byte[] getEncoded();  // 密钥的字节码
}
SecretKeySpec类

用于生成秘钥

案例:

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;// 学习SecretKey
public class Test3 {public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {// 测试数据String data = "Hello World!";// 16字节的秘钥,长度是128,用于AES算法。这个秘钥在这里是明文,但是它通常应该是一个密文byte[] bytes = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);// 生成秘钥SecretKey secretKey = new SecretKeySpec(bytes, "AES");// 加密Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] bytes1 = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));// [115, -27, 109, -94, 63, -59, 24, 123, 100, 86, 107, -90, -29, -34, -10, 126]System.out.println("bytes1 = " + Arrays.toString(bytes1));// 解密cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] bytes2 = cipher.doFinal(bytes1);// Hello World!System.out.println("new String(bytes2) = " + new String(bytes2));}
}
Cipher类

提供了一种通用的API来执行加密操作。

常用方法:

  • init(int opmode, Key key): 初始化 Cipher 对象,指定操作模式(加密或解密)和密钥。
  • doFinal(byte[] input): 完成加密或解密操作,并返回结果。
  • getAlgorithm():获取用于此 Cipher 实例的算法名称。
  • update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset): 加密或解密输入数据,并将结果存储在输出缓冲区中。

案例:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;// Cipher类的使用
public class Test4 {public static void main(String[] args) throws Exception {// 生成秘钥KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(128);SecretKey secretKey = keyGenerator.generateKey();byte[] encoded = secretKey.getEncoded();  // 秘钥// 加密数据String data = "离离原上草";Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 设置加密算法、模式和填充方案cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 初始化加密操作byte[] encodedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));  // 加密// [104, -50, 82, 71, -113, -66, 16, -128, 39, -52, -18, 4, -13, -88, -32, -42]System.out.println("encodedBytes = " + Arrays.toString(encodedBytes));// 解密数据Cipher cipher1 = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher1.init(Cipher.DECRYPT_MODE, secretKey);byte[] decodedBytes = cipher1.doFinal(encodedBytes); // 解密// 离离原上草System.out.println("new String(decodedBytes) = " + new String(decodedBytes));}
}
java.security
SecureRandom类

用于生成安全的随机数,生成的随机数是加密安全的,这些随机数对于加密算法和密钥生成来说是必要的,因为它们需要具有高度不可预测性和不可重复性。当前类是线程安全的。

案例:

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;// 学习SecureRandom类
public class Test2 {public static void main(String[] args) throws NoSuchAlgorithmException {SecureRandom secureRandom = new SecureRandom();// 设置随机数生成器的种子值。种子是随机数生成器的起点,用于决定生成随机数序列的起始状态。// 通过设置相同的种子值,可以确保每次运行程序时生成的随机数序列是相同的。secureRandom.setSeed("123456".getBytes(StandardCharsets.UTF_8));// 生成一个随机字节数组byte[] bytes = new byte[16];secureRandom.nextBytes(bytes);// [107, -76, -125, 126, -73, 67, 41, 16, 94, -28, 86, -115, -38, 125, -58, 126]System.out.println("bytes = " + Arrays.toString(bytes));// 生成一个随机数int i = secureRandom.nextInt();// -758502695System.out.println("i = " + i);// 获取随机数生成器时指定生成随机数的算法,SHA1PRNG是基于SHA1的随机数生成算法。// PRNG:Pseudo Random Number Generator,伪随机数生成器是一种算法或设备,// 用于生成一系列看似随机但实际上具有一定规律的数字序列。这些数字序列对于加密和安全// 应用来说是必要的,因为它们需要具有不可预测性和不可重复性。SecureRandom secureRandom1 = SecureRandom.getInstance("SHA1PRNG");secureRandom1.setSeed("123456".getBytes(StandardCharsets.UTF_8));int i1 = secureRandom1.nextInt();// 1806992254System.out.println("i1 = " + i1);}
}

非对称加密算法

RSA算法

RSA算法:一种非对称加密算法,它的安全性依赖于大数分解的难度。RSA算法使用一对密钥,其中一个密钥是公开的(即公钥),另一个密钥是保密的(即私钥)。公钥用于加密数据,而私钥用于解密数据。RSA是发明这个算法的三个人名字的首字母。

RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

RSA算法的安全性依赖于大数分解的难度,而其可靠性则取决于因数分解的难度。因此,对于极长的密钥(如超过几百位十进制),RSA算法被认为是足够安全的。然而,对于较短的密钥长度(如几十位十进制),存在一些已知的攻击方法,可能会破解密钥。因此,在实际应用中,建议使用足够长的密钥长度以保证安全性。

RSA的主要应用就是对数据进行加密、解密、签名、验签。使用私钥进行签名,使用公钥验签,确保数据来源。

案例:

public class RSAUtil {private static final String RSA = "RSA";private static final String MD5_WITH_RSA = "MD5withRSA";// 初始化公钥和私钥private static final PrivateKey privateKey;private static final PublicKey publicKey;static {try {// 生成公钥和私钥KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);keyPairGenerator.initialize(1024);KeyPair keyPair = keyPairGenerator.generateKeyPair();privateKey = keyPair.getPrivate();publicKey = keyPair.getPublic();} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);}}// 加密public static byte[] encode(String str) {try {Cipher cipher = Cipher.getInstance(RSA);cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 使用公钥进行加密,Cipher设置为加密模式return cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |BadPaddingException e) {throw new RuntimeException(e);}}// 解密public static String decode(byte[] bytes) {try {Cipher cipher = Cipher.getInstance(RSA);cipher.init(Cipher.DECRYPT_MODE, privateKey);  // 使用私钥进行解密,Cipher设置为解密模式return new String(cipher.doFinal(bytes));} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |BadPaddingException e) {throw new RuntimeException(e);}}// 签名,使用私钥签名public static byte[] sign(String data) {try {Signature signature = Signature.getInstance(MD5_WITH_RSA);signature.initSign(privateKey);signature.update(data.getBytes(StandardCharsets.UTF_8));return signature.sign();} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {throw new RuntimeException(e);}}// 验证签名,使用公钥验证签名public static boolean verify(String data, byte[] signs) {try {Signature signature = Signature.getInstance(MD5_WITH_RSA);signature.initVerify(publicKey);signature.update(data.getBytes(StandardCharsets.UTF_8));return signature.verify(signs);} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {throw new RuntimeException(e);}}
}

涉及到的API

java.security
KeyPairGenerator类

生成公钥和私钥,它们是一对的,用于支持非对称加密算法,包括RSA、DSA等

案例:

import java.security.*;public class Test6 {public static void main(String[] args) throws NoSuchAlgorithmException {// 1. 创建一个 KeyPairGenerator 实例,指定 RSA 作为算法KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");// 2. 指定密钥长度,例如2048位,这一步是可选的keyGen.initialize(2048);// 3. 生成密钥对KeyPair keyPair = keyGen.generateKeyPair();// 4. 获取私钥和公钥PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();System.out.println("privateKey = " + privateKey);System.out.println("publicKey = " + publicKey);}
}
Signature类

用于对数据进行签名和验证签名。签名是一种确保数据完整性和身份验证的方法。当用户对数据进行签名时,实际上是在数据使用私钥和数据函数,生成一个唯一的字符串,这个字符串依赖于原始数据。之后,任何人都可以使用相同的函数和用户的公钥来验证签名是否有效。

案例:

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;public class Test8 {public static void main(String[] args) throws Exception {String data = "Hello, World!";// 1. 生成密钥对KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");// 指定秘钥的长度keyGen.initialize(2048);KeyPair pair = keyGen.generateKeyPair();PrivateKey privateKey = pair.getPrivate();PublicKey publicKey = pair.getPublic();// 2. 使用私钥进行签名Signature privateSignature = Signature.getInstance("SHA256withRSA");privateSignature.initSign(privateKey);// 设置数据privateSignature.update(data.getBytes(StandardCharsets.UTF_8));// 生成签名数据byte[] signature = privateSignature.sign();// 3. 使用公钥进行验证Signature publicSignature = Signature.getInstance("SHA256withRSA");publicSignature.initVerify(publicKey);// 设置数据publicSignature.update(data.getBytes());// 验签boolean isValid = publicSignature.verify(signature);System.out.println("验签结果 == " + isValid); // true}
}
PKCS8EncodedKeySpec类

用于解析从 PKCS#8 格式编码的私钥,PKCS#8 是 Public-Key Cryptography Standards (公钥密码学标准) 的一个部分,它定义了私钥的格式。

当用户有一个 PKCS#8 格式的私钥时,可以使用 PKCS8EncodedKeySpec 类来解析它,然后使用 KeyFactory 类来创建一个私钥对象。

案例:

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;public class Test7 {public static void main(String[] args) throws Exception {// 1. 创建一个 KeyPairGenerator 实例,指定 RSA 作为算法KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");// 2. 指定密钥长度,例如2048位,这一步是可选的keyGen.initialize(2048);// 3. 生成密钥对KeyPair keyPair = keyGen.generateKeyPair();// 4. 获取私钥和公钥PrivateKey privateKey = keyPair.getPrivate();// 5. 使用PKCS8EncodedKeySpec解析私钥KeyFactory keyFactory = KeyFactory.getInstance("RSA");PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());PrivateKey privateKey1 = keyFactory.generatePrivate(keySpec);// privateKey == privateKey1System.out.println("privateKey == privateKey1 = " + (privateKey.equals(privateKey1))); // true// sun.security.rsa.RSAPrivateCrtKeyImpl@3f70fSystem.out.println("privateKey = " + privateKey);// sun.security.rsa.RSAPrivateCrtKeyImpl@3f70fSystem.out.println("privateKey1 = " + privateKey1);// 私钥的内容是相同的System.out.println("privateKey.getEncoded() = " + Arrays.toString(privateKey.getEncoded()));System.out.println("privateKey1.getEncoded() = " + Arrays.toString(privateKey1.getEncoded()));}
}
X509EncodedKeySpec

用于从 X.509 格式的秘钥编码中解析密钥。

案例:

import java.security.*;
import java.security.spec.X509EncodedKeySpec;public class Test9 {public static void main(String[] args) throws Exception {// 1. 创建一个 KeyPairGenerator 实例,指定 RSA 作为算法KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");// 2. 指定密钥长度,例如2048位,这一步是可选的keyGen.initialize(2048);// 3. 生成密钥对KeyPair keyPair = keyGen.generateKeyPair();// 4. 获取私钥和公钥PublicKey publicKey = keyPair.getPublic();// 5. 根据公钥中的字节,生成公钥对象KeyFactory keyFactory = KeyFactory.getInstance("RSA");X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getEncoded());PublicKey publicKey1 = keyFactory.generatePublic(keySpec);System.out.println("publicKey1.equals(publicKey) = " + publicKey1.equals(publicKey));}
}

其它:字节数组和字符串之间的转换

使用加密算法加密出的数据是字节数组,为了方便查看字节数组,可以将字节数组转换为字符串,常用的转换方式有两种,Base64编码和将字节数组转换为十六进制字符串

Base64编码

一种基于64个可打印字符来表示二进制数据的表示方法,它用于将二进制数据转换为可打印字符的形式。因为传统的文本编码方式,如ASCII编码,无法直接表示二进制数据,因此在某些情况下,需要一种编码方式,能够将二进制数据转换为文本形式,便于传输和存储。

Base64 中的可打印字符包括字母 A-Z、a-z、0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。

Base64 编码的基本步骤:

  • 将数据划分为3个字节一组,共24位。
  • 将24位数据按照6位一组进行划分,得到4个6位的组,将每个6位的组转换为对应的Base64字符,如果数据不足3字节,进行填充
  • 将所有转换后的 Base64字符连接起来,形成最终的编码结果,编码后的数据长度总是比原始数据长约 1/3

Base64 编码是一种可逆的编码方式,可以通过解码还原原始数据。

案例:

public class Base64Test {@Testpublic void test1() {byte[] bytes = "1234567890".getBytes(StandardCharsets.UTF_8);// 将字节数组编码为字符串String s = Base64.getEncoder().encodeToString(bytes);// MTIzNDU2Nzg5MA==System.out.println("s = " + s);// 将编码后的结果解码为字节数组byte[] decode = Base64.getDecoder().decode(s);// 1234567890System.out.println("new String(decode) = " + new String(decode));}
}

字节数组和十六进制字符串之间的相互转换

另一种常见的处理字节数组的方式,是把它转换为表示十六进制的字符串,一个字节,每4比特转换为一个十六进制的字符。

案例:

// 字节数组转换为十六进制字符串
public static String toHexString(byte[] bytes) {
final char[] HEX_CHAR_ARR = “0123456789ABCDEF”.toCharArray();

    // 将一个字节转换为两个十六进制字符,使用查表法,性能最高StringBuilder sBuilder = new StringBuilder();for (byte b : bytes) {// b无符号右位移时会先被扩展为int,所以结果和0x0F按位与,只取最后四位的值sBuilder.append(HEX_CHAR_ARR[(b >>> 4) & 0x0F])  // 第一个字符.append(HEX_CHAR_ARR[b & 0x0F]);         // 第二个字符}return sBuilder.toString();
}// 十六进制字符串再转换为字节数组
public static byte[] toBytes(String str) {char[] charArray = str.toCharArray();byte[] bytes = new byte[charArray.length / 2];for (int i = 0; i < charArray.length; i += 2) {  // 注意,这里一次前进两位char c = charArray[i];char c1 = charArray[i + 1];// 每两个十六进制数字转换为一个字节// 案例:95A2AA27D3FF301FFF665E98106D2997,在这个字符串中,两个字符代表一个// 字节的数字,在这里截取出第一个字符和第二个字符,按照十六进制数把它们解析为int类,// 然后第一个数字向左位移4位,和第二个数字加在一起,就是一个字节的数据。以此类推。int i1 = (Character.digit(c, 16) << 4) + (Character.digit(c1, 16));bytes[i / 2] = (byte) i1;}return bytes;
}

总结

这里简要介绍了在选择加密解密算法时涉及到的API,如果是程序内部使用的加密解密算法,推荐使用对称加密算法,如果需要和外部交互,比如前端传入的用户密码,可以使用非对称加密算法,前端使用公钥加密用户敏感数据,后端使用私钥解密。字节数组不方便查看,可以使用base64算法将它们转换为字符串

http://www.xdnf.cn/news/641035.html

相关文章:

  • Linux系统中实时查看日志
  • Unity3D仿星露谷物语开发50之初始化农作物
  • day27:零基础学嵌入式之进程
  • Docker镜像存储路径迁移指南(解决磁盘空间不足问题)
  • Nginx安全防护
  • 基于Python Anaconda环境,使用CNN-LSTM模型预测碳交易价格的完整技术方案
  • 大模型与训练与微调
  • Java基础 Day20
  • 嵌入式自学第二十七天
  • ST表——算法的优化
  • TCP 和 UDP 的区别
  • 电梯调度算法详解与Python实现
  • 页表:从虚拟内存到物理内存的转换
  • C语言初阶--操作符
  • 消息队列kafka的基础概念和部署
  • C#、C++、Java、Python 选择哪个好
  • TCP 的三次握手
  • Python Day32 学习
  • 十二、【鸿蒙 NEXT】如何使用系统api实现视频压缩
  • 电子电路:电学都有哪些核心概念?
  • Oracle控制文件损坏恢复方案
  • dify_plugin数据库中的表总结
  • threejs几何体BufferGeometry顶点
  • 【报错】Error attempting to get column ‘created_time‘ from result set.解决方法
  • 手撕string类
  • 汉诺塔超级计算机堆栈区与数据区的协同
  • Docker(零):本文为 “Docker系列” 有关博文的简介和目录
  • Docker核心笔记
  • JavaWeb:SpringBoot配置优先级详解
  • 互联网大厂Java求职面试:AI应用集成中的RAG系统优化与向量数据库性能调优实战