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

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算法将它们转换为字符串

相关文章:

java 加密算法的简单使用

简介 加密算法&#xff0c;就是将原本的明文&#xff0c;通过一系列操作变成密文。在这里介绍一些常用的加密算法。在日常开发中&#xff0c;接触到了一些加密算法&#xff0c;例如&#xff0c;用户的隐私信息&#xff0c;诸如密码、手机号等&#xff0c;需要加密后存储到数据…...

Arduino Uno KY-037声音传感器实验

KY-037声音传感器实验 KY-037声音传感器实验1、 实验内容2、KY-037声音传感器介绍3、实验注意事项4、代码和实验现象 KY-037声音传感器实验 1、 实验内容 通过对KY-037声音传感器吹气&#xff0c;控制LED的打开和关闭&#xff0c;吹一下LED打开&#xff0c;在吹一下LED关闭。…...

机器学习---各算法比较

机器学习算法 线性回归 优点&#xff1a;简单&#xff1b;适用于大规模数据集。 缺点&#xff1a;无法处理非线性关系&#xff1b;对异常值敏感。 多项式回归 优点&#xff1a;捕捉特征和目标之间的非线性关系。 缺点&#xff1a;可能会过度拟合数据。 岭回归 优点&#…...

基于音频Transformer与动作单元的多模态情绪识别算法设计与实现(在RAVDESS数据集上的应用)

摘要&#xff1a;情感识别技术在医学、自动驾驶等多个领域的广泛应用&#xff0c;正吸引着研究界的持续关注。本研究提出了一种融合语音情感识别&#xff08;SER&#xff09;与面部情感识别&#xff08;FER&#xff09;的自动情绪识别系统。在SER方面&#xff0c;我们采用两种迁…...

Flink SQL 计算实时指标同比的实现方法

在 Flink SQL 中计算实时指标的同比(Year-on-Year),核心是通过时间窗口划分周期(如日、月、周),并关联当前周期与去年同期的指标值。以下是结合流数据处理特性的具体实现方法,包含数据准备、窗口聚合、历史数据关联等关键步骤。 一、同比的定义与场景 同比指当前周期指…...

什么是VR实景?有哪些高价值场景?

在数字化浪潮的推动下&#xff0c;虚拟现实技术正以前所未有的速度改变着我们的生活方式和工作模式。 其中&#xff0c;VR实景作为VR技术的一个重要应用场景&#xff0c;独特的沉浸感和交互性&#xff0c;在众多领域展现出应用潜力和高价值场景。什么是VR实景&#xff1f;VR实…...

基于MATLAB实现传统谱减法以及两种改进的谱减法(增益函数谱减法、多带谱减法)的语音增强

基于MATLAB实现传统谱减法以及两种改进的谱减法&#xff08;增益函数谱减法、多带谱减法&#xff09;的语音增强代码示例&#xff1a; 传统谱减法 function enhanced traditional_spectral_subtraction(noisy, fs, wlen, inc, NIS, a, b)% 参数说明&#xff1a;% noisy - 带…...

同一无线网络下的设备IP地址是否相同?

在家庭和办公网络普及的今天&#xff0c;许多人都会好奇&#xff1a;连接同一个Wi-Fi的设备是否共享相同的IP地址&#xff1f;这个问题看似简单&#xff0c;实则涉及多个角度。本文将为您揭示其中的技术奥秘。 用一个无线网IP地址一样吗&#xff1f;同一无线网络&#xff08;如…...

第2周 PINN核心技术揭秘: 如何用神经网络求解偏微分方程

1. PDEs与传统数值方法回顾 (Review of PDEs & Traditional Numerical Methods) 1.1 什么是偏微分方程 (Partial Differential Equations, PDEs)? 偏微分方程是描述自然界和工程领域中各种物理现象(如热量传播、流体流动、波的振动、电磁场分布等)的基本数学语言。 1.…...

【C语言】习题练手套餐 2

每日习题分享。 字符串函数的运用 首先回顾一下字符串函数。 字符串长度 strlen(const char *s);功能&#xff1a;计算字符串的长度&#xff0c;不包含终止符\0。 字符串连接 char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, si…...

[项目总结] 基于Docker与Nginx对项目进行部署

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…...

神经正切核推导(2)

对神经正切核的理解和推导&#xff08;1&#xff09;-CSDN博客 这篇文章包括很多概念的理解 声明&#xff1a; 本篇文章来自于Neural Tangent Kernel &#xff08;NTK&#xff09;基础推导 - Gearlesskai - 博客园 旨在对上述推导过程进行再推导与理解 手写推导部分与其他颜…...

Python模型优化技巧

在机器学习与数据分析领域&#xff0c;模型优化是提升预测准确性、缩短训练时间、降低资源消耗的核心环节。本文结合实战经验&#xff0c;从数据预处理、特征工程、模型调优、代码优化到部署监控&#xff0c;系统梳理Python模型优化的关键技巧&#xff0c;助你打造高效能模型。…...

Redis 面试场景

文章目录 项目地址一、Redis使用场景1.1 统计网站访问次数1.2 产品分类树1.3 分布式锁(常见)1.4 排行榜1.5 记录用户登录状态(记录)1.6 限流1.7 缓存加速1.8消息队列1.9 全局ID生成1.10 订餐系统场景1 . 单体版2. 故事板二、OutBox Pattern2.1 项目3. Saga状态机4. 日志4. …...

MySQL 索引失效及其解决办法

一、前言 在数据库优化中,索引(Index)是一项至关重要的技术手段,可以显著提升查询性能。然而,在实际开发过程中,MySQL 索引并不总是如预期生效。本文将从原理出发,系统地介绍索引失效的常见场景及其解决方案,帮助开发者有效规避性能陷阱。 二、索引基础回顾 MySQL 支…...

Ctrl+鼠标滚动阻止页面放大/缩小

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 一般在我们做大屏的时候&#xff0c;不希望Ctrl鼠标上下滚动的时候页面会放大/缩小&#xff0c;那么在有时候&#xff0c;又不希望影响到别的页面&#xff0c;比如说这个大屏是在另一个管理后台中&am…...

开发积累总结

export default 和export const 均用于从模块导出函数、对象或原始值&#xff0c;区别在于&#xff1a; export default&#xff1a;一个文件中只能有一个&#xff0c;为默认导出&#xff0c;在引用时指定名字。 export const&#xff1a;一个文件中有多个&#xff0c;为命名…...

C++虚函数与类对象模型深度解析

目录 1. 引言 2. 单继承下的虚函数表 2.1 基本概念 2.2 示例分析 3. 多重继承下的虚函数表 3.1 基本概念 3.2 示例分析 4. 虚函数表指针&#xff08;vptr&#xff09;的存储 4.1 单继承 4.2 多重继承 5. 常见面试题解析 问题1&#xff1a;D 继承 B1 和 B2&#xff0…...

3d世界坐标系转屏幕坐标系

世界坐标 ——> NDC标准设备坐标 ——> 屏幕坐标 标准设备NDC坐标系 屏幕坐标系 .project方法将 将向量(坐标)从世界空间投影到相机的标准化设备坐标 (NDC) 空间。 手动实现HTML元素定位到模型位置&#xff0c;实现模型标签效果&#xff08;和css2Render原理同理&#…...

【2025】基于Springboot + vue + 协同过滤算法实现的旅游推荐系统

项目描述 本系统包含管理员和用户两个角色。 管理员角色&#xff1a; 用户管理&#xff1a;管理系统中所有用户的信息&#xff0c;包括添加、删除和修改用户。 配置管理&#xff1a;管理系统配置参数&#xff0c;如上传图片的路径等。 权限管理&#xff1a;分配和管理不同角…...

AI数据治理破局的战略重构

AI数据治理破局的战略重构 AI正在颠覆传统数据治理模式动态策略驱动的AI治理新模式构建AI时代的数据防护栏结语 人工智能正重塑商业世界&#xff0c;那些真正理解当代数据治理变革的企业将占据决定性优势。 旧日的数据治理手册已经无法应对AI时代的全新挑战&#xff0c;我们需要…...

QT6安装与概念介绍

文章目录 前言installModulesQt Core元对象系统属性系统对象模型对象树和所有者信号 & 槽 前言 QT不是纯粹的C标准&#xff0c;它在此基础上引入MOC编译器&#xff0c;在调用C编译器之前会使用该编译器将非C的内容如 Q_OBJECT、signal:等进行处理。此外QT还引入了对象间通…...

Python包管理工具uv 国内源配置

macOS 下 .config/uv/uv.toml内 pip源 [[index]] url "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" default true#uv python install 下载源配置无效&#xff0c;需要在项目里配置 # python-install-mirror "https://mirror.nju.edu.cn/githu…...

ABP VNext + Webhook:订阅与异步回调

&#x1f680; ABP VNext Webhook&#xff1a;订阅与异步回调 &#x1f4da; 目录 &#x1f680; ABP VNext Webhook&#xff1a;订阅与异步回调&#x1f3af; 一、背景切入&#xff1a;如何优雅地支持第三方回调&#xff1f;&#x1f3d7; 二、系统架构设计&#x1f50d; 三…...

Docker(二):开机自启动与基础配置、镜像加速器优化与疑难排查指南

引言 docker 的快速部署与高效运行依赖于两大核心环节&#xff1a;基础环境搭建与镜像生态优化。本期博文从零开始&#xff0c;系统讲解 docker 服务的管理配置与镜像加速实践。第一部分聚焦 docker 服务的安装、权限控制与自启动设置&#xff0c;确保环境稳定可用&#xff1b…...

Lua基础语法

文章目录 一、注释二、 数据类型1. 注意事项2. 全局/局部变量 三、 标识符1. 保留字2. 变量3. 动态类型 四、 运算符1. 算术运算符2. 关系运算符3. 逻辑运算符4. 其他运算符 五、 函数1. 固定参函数2. 可变参函数3. 可返回多个值4. 函数作为参数 六、循环控制语句1. while...do…...

2025年渗透测试面试题总结-匿名[实习]安全工程师(安全厂商)(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 一面技术问题 1. Burp插件原理 2. JavaWeb项目经验 3. CC1-7链原理&#xff08;以CC6为例&#xff0…...

【node.js】实战项目

个人主页&#xff1a;Guiat 归属专栏&#xff1a;node.js 文章目录 1. 项目概览与架构设计1.1 实战项目&#xff1a;企业级电商管理系统1.2 技术栈选择 2. 项目初始化与基础架构2.1 项目结构设计2.2 基础配置管理 3. 用户服务实现3.1 用户服务架构3.2 用户模型设计3.3 用户服务…...

从AD9361 到 ADSY1100 ,中间的迭代产品历史

从 AD9361 到 ADSY1100 的演进&#xff0c;是 Analog Devices&#xff08;ADI&#xff09;在射频收发器&#xff08;RF Transceiver&#xff09;集成化、高性能、宽带宽、低功耗和波束赋形能力方面持续推进的一个路线。以下是其中的重要芯片节点和核心参数对比&#xff1a; 1. …...

免费插件集-illustrator插件-Ai插件-查找选中颜色与pantone中匹配颜色

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件&#xff0c;加强illustrator使用人员工作效率&#xff0c;实现查找选中颜色与pantone中匹配颜色。首先从下载网址下载这款插件https://download.csdn.net/download/m0_6731…...