I Crittografia simmetrica
La crittografia simmetrica e quella asimmetrica sono due metodi di cifratura diversi, che svolgono un ruolo importante nella sicurezza dei dati e nella trasmissione delle informazioni. Di seguito presenterò separatamente queste due tecnologie di cifratura:
1.1 Crittografia simmetrica(crittografia a chiave simmetrica)
La crittografia simmetrica usa la stessa chiave per cifrare e decifrare. Ciò significa che sia il mittente sia il destinatario devono possedere questa chiave per eseguire le operazioni di cifratura e decifratura.
Algoritmi comuni di crittografia simmetrica:
AES(standard di cifratura avanzata)
DES(standard di cifratura dei dati)
3DES (algoritmo di cifratura dei dati triplo)
1.2 Caratteristiche della crittografia simmetrica
In generale, la crittografia simmetrica presenta le seguenti caratteristiche:
Velocità piuttosto elevata:poiché la cifratura e la decifratura usano la stessa chiave, la crittografia simmetrica è generalmente più veloce di quella asimmetrica.
Gestione delle chiavi:la principale difficoltà della cifratura simmetrica risiede nella distribuzione e nella gestione delle chiavi. Se una chiave viene divulgata, la sicurezza della cifratura è minacciata.
Adatto a grandi quantità di dati:Grazie alla sua velocità, la crittografia simmetrica è adatta a cifrare grandi quantità di dati.
Da qui si possono individuare i due principali vantaggi della cifratura simmetrica: il primo è la velocità di calcolo; il secondo è l'idoneità a trattare grandi quantità di dati.
Tuttavia, la crittografia simmetrica presenta un problema cruciale: la gestione delle chiavi. Come trasmettere in sicurezza la chiave dal server al client è un problema! Inoltre, nelle comunicazioni da uno a molti, anche gestire correttamente le chiavi evitando che vengano divulgate rappresenta una sfida. Questo è il limite della crittografia simmetrica.

1.3 Esempio di codice
Ora il fratello Song vi mostrerà come eseguire la cifratura e la decifratura simmetriche con codice Java.
Per implementare la crittografia simmetrica in Java si utilizzano solitamente le classi e le interfacce fornite dalla Java Cryptography Architecture (JCA).
Di seguito un semplice esempio di cifratura e decifratura simmetriche con l'algoritmo AES(Advanced Encryption Standard, standard di cifratura avanzata):
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();
}
}
}Generare la chiave:usare
KeyGeneratorgenera una chiave AES.Crittografia:usare
Cipherper cifrare mediante la classe, convertendo prima i dati in byte e poi cifrandoli, e usando la codifica Base64 per trasformarli in una stringa, così da facilitarne l'archiviazione o la trasmissione.Decifrazione:decodificare la stringa cifrata riconvertendola in byte, quindi decifrarla usando la stessa chiave.
Nel codice precedente occorre prestare attenzione a quanto segue:
La lunghezza della chiave (ad esempio 128 bit) va scelta in base alle esigenze di sicurezza.
Assicurati di conservare le chiavi in modo sicuro e di non inserirle direttamente nel codice.
Per gli ambienti di produzione, è opportuno valutare una strategia di gestione delle chiavi più sicura.
Per motivi di sicurezza, generalmente utilizziamo la soluzione descritta sopra per generare le chiavi. Le chiavi generate in questo modo hanno una caratteristica: cambiano a ogni riavvio del sistema. Se desideri controllarne personalmente la generazione, puoi procedere nel modo seguente:
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");
}In questo modo è possibile controllare la chiave tramite i parametri che si passano.
1.4 Integrazione tra frontend e backend
Quando si usa l'algoritmo AES per cifrare e decifrare tra linguaggi diversi(come JavaScript e Java), è fondamentale assicurarsi che entrambe le parti utilizzino la stessa chiave, la stessa modalità dell'algoritmo(come CBC, ECB, ecc.), lo stesso schema di padding(come PKCS5Padding, PKCS7Padding, ecc.)e lo stesso vettore di inizializzazione(IV, se si usa una modalità che lo richiede, come CBC).
In precedenza, alcuni lettori dicevano di non riuscire mai a decifrare sul backend i dati cifrati sul frontend. Qui Song Ge propone anche un esempio che combina frontend e backend.
Cifratura nel frontend e decifratura nel backend
Codice frontend:
<!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>Codice backend:
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();
}
}
}Tenete presente che la cifratura sul frontend genera un parametro iv, che deve essere trasmesso al backend insieme al risultato della cifratura.
Crittografia nel backend e decrittografia nel frontend
Quando si cifra con AES in Java e si decifra in JavaScript, occorre anche assicurarsi che entrambe le parti utilizzino la stessa chiave, la stessa modalità dell'algoritmo(come CBC, ECB ecc.), lo stesso schema di padding(come PKCS5Padding, PKCS7Padding ecc.)e, se applicabile, lo stesso vettore di inizializzazione(IV).
Codice backend:
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();
}
}
}Codice frontend:
<!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>Ho testato personalmente tutto il codice di cifratura e decifratura per lo scambio tra frontend e backend riportato sopra e funziona senza problemi. Se ne avete bisogno, ricordatevi di salvare questo articolo tra i preferiti: potrà servirvi da riferimento.
II Crittografia asimmetrica
2.1 Che cos'è la crittografia asimmetrica
La crittografia asimmetrica, detta anche crittografia a chiave pubblica, è un metodo di cifratura che utilizza due chiavi diverse: una chiave pubblica e una chiave privata.
In generale, la crittografia asimmetrica presenta le seguenti caratteristiche distintive:
Chiave pubblica e chiave privata
Generazione della chiave pubblica e della chiave privata: la crittografia asimmetrica utilizza una coppia di chiavi; la chiave pubblica è accessibile a chiunque, mentre quella privata è riservata e accessibile soltanto al suo titolare. Le due chiavi sono generate mediante algoritmi matematici e sono correlate, ma non è possibile ricavare l'una dall'altra.
Elevata sicurezza
Difficile da violare: grazie alla complessa relazione matematica tra chiave pubblica e chiave privata, la crittografia asimmetrica offre un elevato livello di sicurezza。Per un aggressore è molto difficile ricavare la chiave privata da quella pubblica, garantendo così la sicurezza dei dati cifrati。
Resistenza agli attacchi quantistici:alcuni progetti blockchain iniziano ad adottare algoritmi crittografici resistenti agli attacchi quantistici, come varianti dell'algoritmo di firma digitale a curva ellittica (ECDSA) sicure contro i computer quantistici, per affrontare le minacce future del calcolo quantistico.
Facilità di distribuzione delle chiavi pubbliche
Carattere pubblico della chiave pubblica: la chiave pubblica può essere resa disponibile a chiunque, quindi è molto facile da distribuire. Chiunque può usarla per cifrare i dati, ma solo il possessore della chiave privata può decifrarli.
Autenticazione e firma digitale
Autenticazione: la chiave pubblica può fungere da identificativo dell'utente, consentendo agli altri di verificarne l'identità senza conoscerne la chiave privata. Questo aiuta a stabilire identità affidabili nelle reti decentralizzate, come le blockchain.
Firma digitale: il titolare della chiave privata può usarla per firmare un messaggio, mentre gli altri possono usare la chiave pubblica per verificare l’autenticità della firma. Questo garantisce l’integrità del messaggio e l’affidabilità della sua provenienza.
Comunicazione sicura
Comunicazione cifrata: la chiave pubblica può essere utilizzata per cifrare i messaggi, che possono essere decifrati soltanto da chi possiede la corrispondente chiave privata. Questo garantisce la sicurezza dei dati durante la comunicazione e impedisce l'accesso alle informazioni da parte di persone non autorizzate.
Controllo degli asset digitali
Indirizzo blockchain e chiave privata: un indirizzo blockchain deriva solitamente da una chiave pubblica; l'utente controlla gli asset digitali associati a quell'indirizzo tramite la chiave privata. La sicurezza della chiave privata è fondamentale per la sicurezza degli asset digitali.
Velocità di cifratura e decifratura
Relativamente lenta: nella crittografia asimmetrica, la cifratura e la decifratura sono molto più lente rispetto alla crittografia simmetrica, poiché richiedono calcoli matematici più complessi. Questo, tuttavia, non ne compromette l'impiego nella comunicazione sicura, nell'autenticazione e in altri ambiti.
2.2 Utilizzi della crittografia asimmetrica
La crittografia asimmetrica ha due scenari d’uso classici.
Crittografia: è l'impiego che conosciamo meglio, ovvero cifrare con la chiave pubblica e decifrare con la chiave privata.
Firma:poiché la rete non è affidabile e i dati possono essere alterati durante la trasmissione, le chiavi pubblica e privata possono essere utilizzate al contrario:si firma il dato con la chiave privata e si verifica la firma con quella pubblica, garantendo la sicurezza e l'integrità dei dati.
Per quanto riguarda il secondo utilizzo, alcuni lo descrivono come cifrare con la chiave privata e decifrare con la chiave pubblica; in ogni caso, tutti capiscono che si sta parlando della stessa cosa.
Gli algoritmi di crittografia asimmetrica, pur potendo essere utilizzati in teoria per la cifratura dei dati e le firme digitali, incontrano nella pratica ostacoli principali nell'elevata complessità computazionale e nella scarsa efficienza.
Rispetto agli algoritmi di crittografia simmetrica, quelli asimmetrici sono più lenti di diversi ordini di grandezza, il che limita notevolmente la loro capacità di gestire grandi quantità di dati. Inoltre, poiché i processi di cifratura e decifratura degli algoritmi asimmetrici sono strettamente legati alla lunghezza della chiave e non supportano modalità di cifratura a blocchi, possono gestire soltanto piccole quantità di dati che non superano la lunghezza della chiave, senza poter cifrare grandi quantità di dati.
Per superare i limiti prestazionali della crittografia asimmetrica, i moderni sistemi crittografici adottano solitamente una strategia ibrida, che combina i vantaggi della crittografia simmetrica e di quella asimmetrica. In questa strategia, la crittografia asimmetrica viene usata principalmente per trasmettere in sicurezza una chiave di crittografia simmetrica all'altra parte (la cosiddetta “negoziazione della chiave”). Una volta che entrambe le parti condividono in sicurezza questa chiave simmetrica, possono usare algoritmi simmetrici efficienti per cifrare e decifrare grandi quantità di dati. Questo uso combinato migliora l'efficienza della cifratura e rafforza anche la sicurezza delle comunicazioni; viene ampiamente impiegato in vari protocolli di comunicazione sicura, come SSL/TLS.
Nel campo delle firme digitali sono stati introdotti anche alcuni adattamenti per migliorare l'efficienza della crittografia asimmetrica。In pratica, prima si calcola un digest dei dati originali (utilizzando, per esempio, MD5 o SHA), ottenendo un valore di lunghezza fissa。Poi si cifra questo digest con un algoritmo di crittografia asimmetrica per generare la firma digitale。Poiché gli algoritmi di digest possono comprimere in modo efficiente dati di qualsiasi lunghezza in un digest di lunghezza fissa, la lunghezza del digest rimane uguale indipendentemente dalle dimensioni dei dati originali, mantenendo efficiente anche il processo di firma。Per verificare la firma basta ricalcolare il digest dei dati originali e confrontarlo con la firma decifrata, così da stabilire rapidamente se i dati sono stati alterati。
2.3 Esempio di cifratura
Esempio di codice Java per cifrare e decifrare con RSA:
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 Coordinamento tra frontend e backend
Cifratura nel frontend e decifratura nel backend
Il codice del backend è identico a quello dell'esempio precedente; la differenza è che, una volta ottenuta la chiave pubblica, possiamo stamparla. Questa chiave pubblica dovrà poi essere trasmessa al 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();
}
}Il codice frontend è il seguente:
<!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>
Crittografia nel backend e decrittografia nel frontend
Cifrare dati con l'algoritmo RSA in Java e decifrarli in JavaScript significa che il server li cifra con la chiave pubblica del frontend e che il frontend li decifra con la propria chiave privata. In questo scenario la chiave privata del frontend può essere rubata facilmente, quindi sconsiglio questo utilizzo. Per questo non fornirò esempi.
Bene, amici, abbiamo ripassato ancora una volta la crittografia simmetrica e asimmetrica. Io, Song Ge, ho testato con successo tutti gli esempi di codice riportati sopra, quindi potete usarli come riferimento.