แอ็พพลิเคชัน Java สามารถและควรใช้คลาสjava.security.SecureRandomเพื่อสร้างค่าสุ่มที่แข็งแกร่งของการเข้ารหัสลับโดยใช้ตัวสร้างตัวเลขสุ่ม ( CSPRNG ) แบบเข้ารหัสลับแบบเข้ารหัสลับที่เข้ารหัสลับ การใช้งาน JDK มาตรฐานของคลาสjava.util.Randomจะไม่ถือว่าเป็น cryptographically strong
ระบบปฏิบัติการแบบ Unix มี/dev/random
ไฟล์พิเศษที่ให้บริการหมายเลขสุ่มหลอกที่มีเสียงรบกวนจากสิ่งแวดล้อมซึ่งรวบรวมจากไดรเวอร์อุปกรณ์และแหล่งอื่น ๆ แต่มันบล็อกถ้ามีเอนโทรปีน้อยลงกว่าที่ร้องขอ ; /dev/urandom
โดยทั่วไปแล้วจะไม่มีทางปิดกั้นแม้ว่าเมล็ดพันธุ์ตัวสร้างหมายเลขเทียมเทียมก็ไม่ได้เริ่มต้นอย่างสมบูรณ์ด้วยเอนโทรปีตั้งแต่เริ่มระบบ ยังคงมีไฟล์พิเศษลำดับที่ 3 /dev/arandom
ซึ่งบล็อกหลังจากบูตจนกว่าเมล็ดจะได้รับการเริ่มต้นอย่างปลอดภัยด้วยเอนโทรปีเพียงพอและจากนั้นจะไม่บล็อกอีกครั้ง
โดยค่าเริ่มต้นเมล็ด JVM ที่SecureRandomระดับการใช้/dev/random
ดังนั้น รหัสของคุณ Java สามารถป้องกันโดยไม่คาดคิด ตัวเลือก-Djava.security.egd=file:/dev/./urandom
ในการเรียกใช้บรรทัดคำสั่งที่ใช้เพื่อเริ่มกระบวนการ Java จะบอกให้ JVM ใช้/dev/urandom
แทน
สิ่งพิเศษที่/./
ดูเหมือนว่าจะทำให้ JVM ใช้อัลกอริทึม SHA1PRNGซึ่งใช้ SHA-1 เป็นรากฐานของ PRNG (Pseudo Random Number Generator) มันแข็งแกร่งกว่าอัลกอริทึม NativePRNG ที่ใช้เมื่อ/dev/urandom
มีการระบุ
ในที่สุดก็มีตำนานว่า/dev/urandom
เป็นหลอกเครื่องกำเนิดไฟฟ้าจำนวนสุ่ม PRNG ขณะที่/dev/random
เป็น“ของจริง” เครื่องกำเนิดไฟฟ้าจำนวนสุ่ม สิ่งนี้ไม่เป็นความจริงทั้งคู่/dev/random
และ/dev/urandom
ถูกป้อนด้วย CSPRNG เดียวกัน (เครื่องสร้างหมายเลขปลอมเทียมแบบเข้ารหัสลับ) เฉพาะพฤติกรรมเมื่อสระว่ายน้ำของพวกเขาหมดเอนโทรปีตามการประมาณการบางอย่างแตกต่าง: /dev/random
บล็อกในขณะที่/dev/urandom
ไม่
แล้วเอนโทรปีทำงานต่ำแค่ไหน? มันไม่สำคัญ
ปรากฎว่า "ดูสุ่ม" เป็นข้อกำหนดขั้นพื้นฐานสำหรับการสร้างแบบเข้ารหัสจำนวนมากของเรา และถ้าคุณรับเอาท์พุทของแฮชการเข้ารหัสมันจะต้องแยกไม่ออกจากสตริงการสุ่มเพื่อที่ยันต์จะยอมรับมัน นั่นเป็นเหตุผลของการใช้อัลกอริทึม SHA1PRNG เนื่องจากใช้ฟังก์ชันแฮชและตัวนับพร้อมกับเมล็ด
ควรใช้เมื่อใด
ฉันจะบอกเสมอ
แหล่งที่มา:
https://gist.github.com/svrc/5a8accc57219b9548fe1
https://www.2uo.de/myths-about-urandom
แก้ไข 04/2020:
ความคิดเห็นกล่าวถึงการเปลี่ยนแปลงพฤติกรรมของระดับSecureRandomใน Java 8
SHA1PRNG และ NativePRNG ได้รับการแก้ไขเพื่อให้สอดคล้องกับคุณสมบัติแหล่งที่มาของเมล็ด SecureRandom ในไฟล์ java.security (วิธีแก้ปัญหาที่คลุมเครือโดยใช้ไฟล์: /// dev / urandom และ file: / dev /./ urandom ไม่จำเป็นอีกต่อไป)
สิ่งนี้ได้รับการชี้ให้เห็นแล้วจากการทดสอบที่อ้างถึงในส่วนแหล่งข้อมูลด้านบน /./
จำเป็นต้องมีการเพิ่มเติมเพื่อเปลี่ยนอัลกอริทึมที่ใช้โดยSecureRanomใน Java 8 จาก NativePRNG เป็น SHA1PRNG
อย่างไรก็ตามฉันมีข่าวบางอย่างที่ฉันต้องการแบ่งปัน เป็นต่อJEP-273ตั้งแต่ Java 9 SecureRandomการดำเนินการระดับสามบิตตายตัว Generator สุ่ม (DRBG)กลไกที่อธิบายไว้ในNIST 800-90Ar1 กลไกเหล่านี้ใช้อัลกอริธึมที่ทันสมัยเช่นเดียวกับ SHA-512 และ AES-256
JDK มีการใช้งานSecureRandomสองประเภท:
- หนึ่งคือขึ้นอยู่กับแพลตฟอร์มและขึ้นอยู่กับการโทรพื้นเมืองหรืออุปกรณ์ OS เช่นการอ่าน
/dev/{u}random
บน Unix หรือการใช้ CryptoAPI บน Windows รุ่นล่าสุดของ Linux และ Windows แล้วสนับสนุน DRBG, แต่รุ่นเก่าและระบบฝังตัวอาจจะไม่
- อีกประเภทหนึ่งคือการใช้งานจาวาบริสุทธิ์ซึ่งใช้การใช้งาน RNG แบบเก่าที่ใช้ SHA1 ซึ่งไม่รุนแรงเท่ากับอัลกอริทึมที่ใช้โดยกลไก DRBG ที่ได้รับอนุมัติ
ในขณะเดียวกันคู่มือ Java 13 การรักษาความปลอดภัยของนักพัฒนายังคงอ่าน
บน Linux และ macOS หากอุปกรณ์การรวบรวมเอนโทรปีใน java.security ถูกตั้งค่าเป็นfile:/dev/urandom
หรือแสดงfile:/dev/random
ว่าต้องการ NativePRNG เป็น SHA1PRNG มิฉะนั้นแนะนำให้ใช้ SHA1PRNG
เพื่อให้ชัดเจนว่ากลไก DRBG ใหม่ทำงานร่วมกับ PRNG ก่อนหน้านี้ได้อย่างไรฉันจึงทำการทดสอบบน macOS (ดาร์วิน) ด้วย AdoptOpenJDK (build 13.0.2 + 8) นี่คือผลลัพธ์:
ไฟล์: / dev / random
ลำดับความพึงพอใจสำหรับผู้ให้บริการ:
SecureRandom.NativePRNG
SecureRandom.DRBG
SecureRandom.SHA1PRNG
ไฟล์: / dev / urandom
ลำดับการตั้งค่าสำหรับผู้ให้บริการ:
SecureRandom.NativePRNG
SecureRandom.DRBG
SecureRandom.SHA1PRNG
ไฟล์: / dev /./ urandom
ลำดับการตั้งค่าสำหรับผู้ให้บริการ:
SecureRandom.DRBG
SecureRandom.SHA1PRNG
SecureRandom.NativePRNG
สรุป:
ฉันขอแนะนำให้ใช้-Djava.security.egd=file:/dev/./urandom
เพื่อให้แน่ใจว่าใช้ประโยชน์จากการใช้งานSecureRandom ที่แข็งแกร่งที่สุดโดยไม่คำนึงถึงแพลตฟอร์มที่ใช้ในขณะที่หลีกเลี่ยงการบล็อกรหัสโดยไม่คาดคิด