ในคำตอบนี้ฉันเลือกที่จะเข้าใกล้ธีมหลัก "Simple Java AES encrypt / decrypt example" ไม่ใช่คำถามเฉพาะสำหรับการดีบักเพราะฉันคิดว่าสิ่งนี้จะเป็นประโยชน์ต่อผู้อ่านส่วนใหญ่
นี่คือสรุปง่ายๆในบล็อกโพสต์ของฉันเกี่ยวกับการเข้ารหัส AES ใน Javaดังนั้นฉันขอแนะนำให้อ่านก่อนที่จะนำไปใช้ อย่างไรก็ตามฉันจะยังคงให้ตัวอย่างง่ายๆในการใช้งานและให้คำแนะนำในสิ่งที่ต้องระวัง
ในตัวอย่างนี้ฉันจะเลือกใช้การเข้ารหัสที่ผ่านการรับรองความถูกต้องกับGalois / Counter Mode หรือโหมดGCM เหตุผลก็คือในกรณีส่วนใหญ่คุณต้องการ ความสมบูรณ์และความถูกต้องร่วมกับการรักษาความลับ (อ่านเพิ่มเติมในบล็อก )
การสอนการเข้ารหัส / ถอดรหัส AES-GCM
นี่เป็นขั้นตอนที่จำเป็นในการเข้ารหัส / ถอดรหัสกับAES-GCMกับJava Cryptography Architecture (JCA) อย่าผสมกับตัวอย่างอื่น ๆเนื่องจากความแตกต่างเล็กน้อยอาจทำให้โค้ดของคุณไม่ปลอดภัยอย่างที่สุด
1. สร้างคีย์
เนื่องจากขึ้นอยู่กับกรณีการใช้งานของคุณฉันจะถือว่ากรณีที่ง่ายที่สุดนั่นคือคีย์ลับแบบสุ่ม
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");
สำคัญ:
2. สร้างเวกเตอร์เริ่มต้น
เวกเตอร์เริ่มต้น (IV)ถูกนำมาใช้เพื่อให้คีย์ลับเดียวกันจะสร้างที่แตกต่างกันตำราตัวเลข
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
สำคัญ:
3. เข้ารหัสด้วย IV และ Key
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);
สำคัญ:
- ใช้แท็กการตรวจสอบความถูกต้อง16 ไบต์ / 128 บิต(ใช้เพื่อตรวจสอบความสมบูรณ์ / ความถูกต้อง)
- แท็กการตรวจสอบความถูกต้องจะต่อท้ายข้อความเข้ารหัสโดยอัตโนมัติ (ในการใช้งาน JCA)
- เนื่องจาก GCM ทำงานเหมือนรหัสของสตรีมจึงไม่จำเป็นต้องมีช่องว่างภายใน
- ใช้
CipherInputStream
เมื่อเข้ารหัสข้อมูลจำนวนมาก
- ต้องการตรวจสอบข้อมูลเพิ่มเติม (ไม่เป็นความลับ) ว่ามีการเปลี่ยนแปลงหรือไม่ คุณอาจต้องการใช้ข้อมูลที่เกี่ยวข้องกับ
cipher.updateAAD(associatedData);
More here
3. จัดลำดับเป็นข้อความเดียว
เพียงต่อท้าย IV และ ciphertext ตามที่ระบุไว้ข้างต้น IV ไม่จำเป็นต้องเป็นความลับ
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
เลือกที่จะเข้ารหัสด้วยBase64หากคุณต้องการการแสดงสตริง ใช้การติดตั้งในตัวของ AndroidหรือJava 8 (อย่าใช้ Apache Commons Codec - เป็นการใช้งานที่แย่มาก) การเข้ารหัสใช้เพื่อ "แปลง" ไบต์อาร์เรย์เป็นการแสดงสตริงเพื่อให้ ASCII ปลอดภัยเช่น:
String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);
4. เตรียมถอดรหัส: Deserialize
หากคุณเข้ารหัสข้อความก่อนอื่นให้ถอดรหัสเป็นไบต์อาร์เรย์:
byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)
สำคัญ:
5. ถอดรหัส
เริ่มต้นการเข้ารหัสและตั้งค่าพารามิเตอร์เดียวกันกับการเข้ารหัส:
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);
สำคัญ:
- ไม่ลืมที่จะเพิ่มข้อมูลที่เกี่ยวข้องกับ
cipher.updateAAD(associatedData);
ถ้าคุณเพิ่มมันในระหว่างการเข้ารหัส
คุณสามารถดูข้อมูลโค้ดที่ใช้งานได้ในส่วนสำคัญนี้
โปรดทราบว่าการใช้งาน Android (SDK 21+) และ Java (7+) ล่าสุดควรมี AES-GCM รุ่นเก่าอาจขาดมัน ฉันยังคงเลือกโหมดนี้เนื่องจากใช้งานได้ง่ายกว่านอกจากจะมีประสิทธิภาพมากกว่าเมื่อเทียบกับโหมดเข้ารหัสที่คล้ายกันของMac (เช่นAES-CBC + HMAC ) ดูบทความนี้เกี่ยวกับวิธีใช้ AES-CBC กับ HMAC