I Symmetric Encryption
Symmetric encryption and asymmetric encryption are two different encryption methods, both playing important roles in data security and information transmission. Below, I will introduce each of these encryption techniques:
1.1 Symmetric Encryption
Symmetric encryption uses the same key for both encryption and decryption. This means that both the sender and the recipient must possess this key to perform encryption and decryption.
Common symmetric encryption algorithms:
AES (Advanced Encryption Standard)
DES (Data Encryption Standard)
3DES (Triple Data Encryption Algorithm)
1.2 Characteristics of symmetric encryption
Generally, symmetric encryption has the following characteristics:
Relatively fast:Because encryption and decryption use the same key, symmetric encryption is generally faster than asymmetric encryption.
Key Management:The main challenge in symmetric encryption lies in key distribution and management. If the key is leaked, the security of the encryption is threatened.
Suitable for large volumes of data:Because of its speed, symmetric encryption is suitable for encrypting large amounts of data.
From this, we can see that symmetric encryption has two main advantages: first, fast computation; second, suitability for large amounts of data.
However, symmetric encryption has a fatal problem: key management. Safely transmitting the key from the server to the client is a problem! Managing keys to prevent leaks during one-to-many communication is another challenge. These are shortcomings of symmetric encryption.

1.3 Code Examples
Next, I'll show you how to perform symmetric encryption and decryption in Java.
Symmetric encryption in Java is typically implemented using the classes and interfaces provided by the Java Cryptography Architecture (JCA).
Below is a simple example of symmetric encryption and decryption using AES (Advanced Encryption Standard):
public class SymmetricEncryptionExample { // 生成密钥
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 可以是128, 192或256位
return keyGenerator.generateKey();
}
// 加密方法
public static String encrypt(String data, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 解密方法
public static String decrypt(String encryptedData, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes);
}
public static void main(String[] args) {
try {
// 生成密钥
SecretKey key = generateKey();
// 原始数据
String originalData = "Hello, JavaBoy!";
// 加密
String encryptedData = encrypt(originalData, key);
System.out.println("加密数据: " + encryptedData);
// 解密
String decryptedData = decrypt(encryptedData, key);
System.out.println("解密数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}
}Generate a key: use
KeyGeneratorGenerate an AES key.Encryption: use
Cipherclass to encrypt the data after converting it to bytes, then encode it as a Base64 string for convenient storage or transmission.Decryption: Decode the encrypted string back into bytes, then decrypt it using the same key.
Here is what you need to pay attention to in the code above:
The key length (such as 128 bits) should be chosen according to security requirements.
Ensure that keys are stored securely; do not hardcode them in your code.
For production environments, consider using a more secure key management strategy.
For security reasons, we generally use the method above to generate keys. One characteristic of keys generated this way is that they change every time the system restarts. If you want to control key generation yourself, you can generate a key as follows:
public static SecretKey generateKeyFromPassword(String password, int keySize) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), "salt".getBytes(), 65536, keySize);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}This lets you control the key through parameters you supply yourself.
1.4 Coordinating the frontend and backend
When using AES for encryption and decryption across languages (such as JavaScript and Java), the key is to ensure both sides use the same key, cipher mode (such as CBC or ECB), padding scheme (such as PKCS5Padding or PKCS7Padding), and initialization vector (IV, if using a mode that requires one, such as CBC).
Some readers previously said that their backend was consistently unable to decrypt data encrypted by the frontend, so I'll also provide an example showing the frontend and backend working together.
Frontend encryption and backend decryption
Frontend code:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>AES加密示例</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
</head>
<body>
<script>
function encryptAES(text, secretKey) {
const key = CryptoJS.enc.Utf8.parse(secretKey);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
// 对于CBC模式,需要IV
const encrypted = CryptoJS.AES.encrypt(text, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回加密后的文本和IV(Base64格式),实际使用中可能需要安全地传输这些值
return {
ciphertext: encrypted.toString(),
iv: iv.toString(CryptoJS.enc.Base64)
};
}
const secretKey = 'helloworldhelloworldhelloworld11'; /
/ 确保密钥是32个字符长(256位)
const text = 'Hello, javaboy!';
const result = encryptAES(text, secretKey);
console.log(result);
</script>
</body>
</html>Backend code:
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class AESDecrypt { public static String decryptAES(String encryptedData, String secretKey, String iv) throws Exception { IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv)); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] decodedValue = Base64.getDecoder().decode(encryptedData); byte[] decryptedValue = cipher.doFinal(decodedValue); return new String(decryptedValue, "UTF-8");
} public static void main(String[] args) { try { //前端加密后的文本
String encryptedText = "PYANpAjMsRnBIEhovtEXQw=="; String secretKey = "helloworldhelloworldhelloworld11"; //前端 IV,要和加密后的文本一起传到后端
String iv = "y/jUHcgSOpOiyNlsfjNUBg=="; String decryptedText = decryptAES(encryptedText, secretKey, iv);
System.out.println("解密文本: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}Note that frontend encryption generates an iv parameter, which must be passed to the backend along with the encrypted result.
Backend Encryption and Frontend Decryption
When encrypting with AES in Java and decrypting in JavaScript, you likewise need to ensure that both sides use the same key, cipher mode (such as CBC or ECB), padding scheme (such as PKCS5Padding or PKCS7Padding), and, if applicable, initialization vector (IV).
Backend code:
public class AESEncrypt { public static String encryptAES(String plainText, String secretKey, String iv) throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256); // 对于AES-256
SecretKey secret = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv)); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivParameterSpec); byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encrypted);
} public static void main(String[] args) { try { String plainText = "Hello, 江南一点雨!"; String secretKey = "helloworldhelloworldhelloworld11"; // 确保密钥是32个字符长(256位)
String iv = Base64.getEncoder().encodeToString(new byte[16]); // 示例IV,实际应用中应更安全地生成
String encryptedText = encryptAES(plainText, secretKey, iv);
System.out.println("Encrypted text: " + encryptedText);
System.out.println("IV (Base64): " + iv); // 确保将IV发送给解密方
} catch (Exception e) {
e.printStackTrace();
}
}
}Frontend code:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script>
function decryptAES(ciphertext, secretKey, iv) { const key = CryptoJS.enc.Utf8.parse(secretKey); const ivParsed = CryptoJS.enc.Base64.parse(iv); const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
},
key,
{ iv: ivParsed, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
}
); return decrypted.toString(CryptoJS.enc.Utf8);
} const secretKey = 'helloworldhelloworldhelloworld11'; const iv = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 从Java代码获取
const ciphertext = '1lPNVF1injas78KUWeKp5FusEr6f0pGgcrLAg9ELFr8='; // 从Java代码获取
const decryptedText = decryptAES(ciphertext, secretKey, iv); console.log(decryptedText); </script></head><body></body></html>I, Song Ge, have personally tested all the frontend–backend encryption and decryption code above, and it works without issues. If you need this functionality, remember to bookmark this article for reference.
II Asymmetric Encryption
2.1 What Is Asymmetric Encryption
Asymmetric encryption, also known as public-key encryption, is an encryption method that uses two different keys: a public key and a private key.
Generally speaking, asymmetric encryption has the following distinct characteristics:
Public and Private Keys
Generating public and private keys: Asymmetric encryption uses a pair of keys. The public key is public and accessible to anyone, while the private key is private and accessible only to its holder. These two keys are generated by mathematical algorithms and are related, but neither can be derived from the other.
High security
Difficult to crack: The complex mathematical relationship between the public and private keys gives asymmetric encryption a high level of security. Attackers have great difficulty deriving the private key from the public key, thus keeping encrypted data secure.
Resistance to quantum computing attacks: Some blockchain projects have begun adopting cryptographic algorithms resistant to quantum computing attacks, such as quantum-safe variants of the Elliptic Curve Digital Signature Algorithm (ECDSA), to address the threat of future quantum computing.
Public keys are easy to distribute
The public nature of the public key: The public key can be made available to anyone, making it very convenient to distribute. Anyone can use the public key to encrypt data, but only the private key holder can decrypt it.
Authentication and Digital Signatures
Authentication: A public key can serve as a user's identifier, allowing others to verify the user's identity without knowing their private key. This helps establish trusted identities in decentralized networks such as blockchains.
Digital signatures: The private key holder can use the private key to sign a message, while others can use the public key to verify the signature's authenticity. This ensures the message's integrity and the reliability of its source.
Secure communication
Encrypted communication: A public key can be used to encrypt messages, and only someone holding the corresponding private key can decrypt them. This ensures data security during communication and prevents unauthorized access to information.
Digital Asset Control
Blockchain addresses and private keys: A blockchain address is usually derived from a public key, and users control the digital assets associated with that address through the private key. The security of the private key is crucial to the security of digital assets.
Encryption and decryption speed
Relatively slow: Asymmetric encryption is much slower than symmetric encryption at encrypting and decrypting because it requires more complex mathematical calculations. However, this does not prevent its use in secure communications, authentication, and other fields.
2.2 Uses of Asymmetric Encryption
Asymmetric encryption has two classic use cases.
Encryption: This is the use we know best—encrypting with the public key and decrypting with the private key.
Signing: Since networks cannot be trusted, data may be tampered with during transmission. In this case, the public and private keys can be used in reverse: the private key signs the data, and the public key verifies the signature, ensuring the data's security and integrity.
For the second use, some people describe it as encrypting with the private key and decrypting with the public key; anyway, everyone knows we are talking about the same thing.
Although asymmetric encryption algorithms can theoretically be used for data encryption and digital signatures, their high computational complexity and low efficiency become major obstacles in practice.
Compared with symmetric encryption algorithms, asymmetric encryption is several orders of magnitude slower, which greatly limits its ability to process large volumes of data. In addition, because the encryption and decryption processes of asymmetric algorithms are closely tied to key length and do not support block encryption modes, they can handle only small amounts of data no longer than the key length and cannot encrypt large volumes of data.
To overcome the performance limitations of asymmetric encryption, modern encryption systems usually adopt a hybrid encryption strategy that combines the advantages of symmetric and asymmetric encryption. In this strategy, asymmetric encryption is mainly used to securely transmit a symmetric encryption key (known as “key negotiation”) to the other party. Once both parties securely share this symmetric key, they can use efficient symmetric encryption algorithms to encrypt and decrypt large amounts of data. This combined approach improves encryption efficiency and enhances communication security, and is widely used in secure communication protocols such as SSL/TLS.
In digital signatures, adaptations have also been made to improve the efficiency of asymmetric encryption. Specifically, the original data is first hashed (using MD5, SHA, or similar algorithms) to obtain a fixed-length digest. An asymmetric encryption algorithm is then used to encrypt this digest, generating a digital signature. Because hash algorithms efficiently compress data of any length into a fixed-length digest, the digest has the same length regardless of the size of the original data, keeping the signing process efficient. To verify a signature, simply recalculate the digest of the original data and compare it with the decrypted signature to quickly determine whether the data has been tampered with.
2.3 Encryption Example
Example of RSA encryption and decryption in Java:
public class RsaDemo { public static void main(String[] args) { try { KeyPair keyPair = generateRSAKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); String originalData = "Hello, 江南一点雨!"; String encryptedData = encrypt(publicKey, originalData); String decryptedData = decrypt(privateKey, encryptedData);
System.out.println("加密后的数据: " + encryptedData);
System.out.println("解密后的数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
} public static String decrypt(PrivateKey privateKey, String encryptedData) throws Exception { Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData)); return new String(decryptedBytes);
} public static String encrypt(PublicKey publicKey, String data) throws Exception { Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(data.getBytes()); return Base64.getEncoder().encodeToString(bytes);
} public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048); // 可以指定密钥长度,如2048位
return keyGen.generateKeyPair();
}
}2.4 Frontend and Backend Integration
Frontend encryption and backend decryption
The backend code is the same as in the example above. The difference is that after obtaining the public key, we can print it out; this public key will later need to be passed to the frontend:
public static void main(String[] args) { try { KeyPair keyPair = generateRSAKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate();
System.out.println("公钥: " + Base64.getEncoder().encodeToString(publicKey.getEncoded())); String originalData = "Hello, 江南一点雨!"; String encryptedData = encrypt(publicKey, originalData); String decryptedData = decrypt(privateKey, encryptedData);
System.out.println("加密后的数据: " + encryptedData);
System.out.println("解密后的数据: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}The frontend code is as follows:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>RSA Encryption Example</title> <!-- 引入jsencrypt库 --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/bin/jsencrypt.min.js"></script></head><body><script> // 重写前端加密方法 function encryptData(publicKey, data) { // 创建一个新的JSEncrypt对象 var encryptor = new JSEncrypt(); // 设置公钥 encryptor.setPublicKey(publicKey); // 加密数据 var encrypted = encryptor.encrypt(data); return encrypted; } // 示例公钥(实际使用时应该替换为服务器提供的公钥) var publicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnP5FOXIghidLDE2FgFwzi4Y+qTdYjnc0eMoiL5i4gdOeTE+7Uu9YvDB51GcBWKD9lvmZr5rX8z0OBpwe526qsTekNpXoIVk9DB34US0HOrAXEEwpUTVuSR656cJGAwmWBkVaalQynEz4Dlzrm53zExVYueruYUuzFyuZDaQcFnl3rWUH/XDkCIe23z0R1TfT3Q2OYNKft0u56r0S/ko99utXuYJK9yowe7QGT6q4cSJwsITQTomCARAq9q+bSNuGEYa4FlYCKIKWIhKbMhz0FYIMB2fJN10GyZbbvKASqeMkuCoD2Efgd8/6uMwOaMcx9LgEkcFaQ3qgDutsPXNUswIDAQAB`; // 要加密的数据 var data = 'Hello, javaboy!'; // 调用加密方法 var encryptedData = encryptData(publicKey, data); // 输出加密后的数据 console.log('Encrypted Data:', encryptedData);</script></body></html>
Backend Encryption and Frontend Decryption
Encrypting data with RSA in Java and decrypting it in JavaScript means the server encrypts it with the frontend's public key, and the frontend decrypts it with its own private key. In this scenario, the frontend's private key can easily be stolen, so this usage is not recommended. I won't give an example.
All right, we've gone over symmetric and asymmetric encryption together once again. I've tested all the sample code above successfully, so you can use it as a reference.