APK Signature Scheme v2

APK Signature Scheme v2 เป็นรูปแบบลายเซ็นทั้งไฟล์ที่เพิ่มความเร็วในการตรวจสอบและ เพิ่มการรับประกันความสมบูรณ์ โดยการตรวจจับการเปลี่ยนแปลงในส่วนที่ได้รับการคุ้มครองของ APK

การลงนามโดยใช้ APK Signature Scheme v2 จะแทรก APK Signing Block ลงในไฟล์ APK ทันทีก่อนส่วน ZIP Central Directory ภายใน APK Signing Block ลายเซ็น v2 และข้อมูลประจำตัวของผู้ลงนามจะถูกจัดเก็บไว้ใน APK Signature Scheme v2 Block

APK ก่อนและหลังการลงนาม

รูปที่ 1 APK ก่อนและหลังการลงนาม

APK Signature Scheme v2 เปิดตัวใน Android 7.0 (Nougat) หากต้องการให้ APK ติดตั้งได้บน Android 6.0 (Marshmallow) และอุปกรณ์รุ่นเก่า ควรลงนาม APK โดยใช้ การลงนาม JAR ก่อนที่จะลงนามด้วยรูปแบบ v2

บล็อกการลงนาม APK

เพื่อรักษาความเข้ากันได้แบบย้อนหลังกับรูปแบบ APK v1 ลายเซ็น v2 และ APK ที่ใหม่กว่าจะถูกจัดเก็บไว้ใน APK Signing Block ซึ่งเป็นคอนเทนเนอร์ใหม่ที่ได้รับการแนะนำเพื่อรองรับ APK Signature Scheme v2 ในไฟล์ APK APK Signing Block จะอยู่ด้านหน้า ZIP Central Directory ซึ่งอยู่ที่ส่วนท้ายของไฟล์

บล็อกประกอบด้วยคู่ค่า ID ในลักษณะที่ช่วยให้ค้นหาบล็อกใน APK ได้ง่ายขึ้น ลายเซ็น v2 ของ APK จะถูกจัดเก็บเป็นคู่ค่า ID ที่มี ID 0x7109871a

รูปแบบ

รูปแบบของ APK Signing Block จะเป็นดังนี้ (ช่องตัวเลขทั้งหมดเป็นแบบปลายเล็ก):

  • size of block เป็นไบต์ (ไม่รวมฟิลด์นี้) (uint64)
  • ลำดับของคู่ ID-ค่าที่นำหน้าความยาว uint64:
    • ID (uint32)
    • value (ความยาวผันแปร: ความยาวของคู่ - 4 ไบต์)
  • size of block เป็นไบต์ - เหมือนกับฟิลด์แรก (uint64)
  • magic “APK Sig Block 42” (16 ไบต์)

APK จะแยกวิเคราะห์โดยการค้นหาจุดเริ่มต้นของ ZIP Central Directory ก่อน (โดยการค้นหา ZIP End ของบันทึก Central Directory ที่ส่วนท้ายของไฟล์ จากนั้นอ่านค่าออฟเซ็ตเริ่มต้นของ Central Directory จากบันทึก) คุณค่า magic นี้เป็นวิธีที่รวดเร็วในการระบุว่าสิ่งที่อยู่ก่อนหน้า Central Directory น่าจะเป็น APK Signing Block size of block จะชี้ไปที่จุดเริ่มต้นของบล็อกในไฟล์อย่างมีประสิทธิภาพ

คู่ค่า ID กับ ID ที่ไม่รู้จักควรถูกละเว้นเมื่อตีความบล็อก

APK Signature Scheme v2 บล็อก

APK ได้รับการลงนามโดยผู้ลงนาม/ข้อมูลประจำตัวตั้งแต่ 1 รายขึ้นไป โดยแต่ละรายจะแสดงด้วยคีย์การลงนาม ข้อมูลนี้จะถูกจัดเก็บเป็น APK Signature Scheme v2 Block สำหรับผู้ลงนามแต่ละคน ข้อมูลต่อไปนี้จะถูกจัดเก็บ:

  • (อัลกอริธึมลายเซ็น, ไดเจสต์, ลายเซ็น) สิ่งอันดับ ข้อมูลสรุปจะถูกเก็บไว้เพื่อแยกการตรวจสอบลายเซ็นจากการตรวจสอบความสมบูรณ์ของเนื้อหาของ APK
  • กลุ่มใบรับรอง X.509 ที่แสดงตัวตนของผู้ลงนาม
  • แอตทริบิวต์เพิ่มเติมเป็นคู่คีย์-ค่า

สำหรับผู้ลงนามแต่ละราย APK จะได้รับการตรวจสอบโดยใช้ลายเซ็นที่รองรับจากรายการที่ให้ไว้ ลายเซ็นที่มีอัลกอริธึมลายเซ็นที่ไม่รู้จักจะถูกละเว้น ขึ้นอยู่กับการใช้งานแต่ละรายการในการเลือกลายเซ็นที่จะใช้เมื่อพบลายเซ็นที่รองรับหลายรายการ สิ่งนี้ทำให้สามารถแนะนำวิธีการลงนามที่แข็งแกร่งยิ่งขึ้นในอนาคตด้วยวิธีที่เข้ากันได้แบบย้อนหลัง แนวทางที่แนะนำคือการตรวจสอบลายเซ็นที่แข็งแกร่งที่สุด

รูปแบบ

APK Signature Scheme v2 Block ถูกจัดเก็บไว้ใน APK Signing Block ภายใต้ ID 0x7109871a

รูปแบบของ APK Signature Scheme v2 Block มีดังนี้ (ค่าตัวเลขทั้งหมดเป็น little-endian ส่วนช่องที่นำหน้าความยาวทั้งหมดจะใช้ uint32 สำหรับความยาว):

  • ลำดับคำนำหน้าความยาวของ signer ที่นำหน้าความยาว :
    • signed data หน้าด้วยความยาว :
      • ลำดับที่นำหน้าความยาวของ digests ที่นำหน้าด้วยความยาว :
      • ลำดับความยาวนำหน้าของ certificates X.509 :
        • certificate X.509 ที่นำหน้าความยาว (แบบฟอร์ม ASN.1 DER)
      • ลำดับความยาวนำหน้า additional attributes ที่นำหน้าความยาว:
        • ID (uint32)
        • value (ความยาวตัวแปร: ความยาวของแอตทริบิวต์เพิ่มเติม - 4 ไบต์)
    • ลำดับความยาวนำหน้าของ signatures ที่นำหน้าความยาว :
      • signature algorithm ID (uint32)
      • signature ที่มีความยาวนำหน้าเหนือ signed data
    • public key ที่มีความยาวนำหน้า (SubjectPublicKeyInfo, แบบฟอร์ม ASN.1 DER)

รหัสอัลกอริทึมลายเซ็น

  • 0x0101—RSASSA-PSS พร้อมการแยกย่อย SHA2-256, SHA2-256 MGF1, Salt 32 ไบต์, ตัวอย่าง: 0xbc
  • 0x0102—RSASSA-PSS พร้อมไดเจสต์ SHA2-512, SHA2-512 MGF1, Salt 64 ไบต์, ตัวอย่าง: 0xbc
  • 0x0103—RSASSA-PKCS1-v1_5 พร้อมการแยกย่อย SHA2-256 นี่ใช้สำหรับระบบบิลด์ที่จำเป็นต้องมีลายเซ็นที่กำหนด
  • 0x0104—RSASSA-PKCS1-v1_5 พร้อมการแยกย่อย SHA2-512 นี่ใช้สำหรับระบบบิลด์ที่จำเป็นต้องมีลายเซ็นที่กำหนด
  • 0x0201—ECDSA พร้อมการแยกย่อย SHA2-256
  • 0x0202—ECDSA พร้อมการแยกย่อย SHA2-512
  • 0x0301—DSA พร้อมการแยกย่อย SHA2-256

อัลกอริธึมลายเซ็นข้างต้นทั้งหมดได้รับการสนับสนุนโดยแพลตฟอร์ม Android เครื่องมือการลงนามอาจสนับสนุนชุดย่อยของอัลกอริทึม

ขนาดคีย์ที่รองรับและเส้นโค้ง EC:

  • อาร์เอสเอ: 1024, 2048, 4096, 8192, 16384
  • EC: NIST P-256, P-384, P-521
  • ส.ส.: 1024, 2048, 3072

เนื้อหาที่ได้รับการคุ้มครองความสมบูรณ์

เพื่อวัตถุประสงค์ในการปกป้องเนื้อหา APK APK ประกอบด้วยสี่ส่วน:

  1. เนื้อหาของรายการ ZIP (จากออฟเซ็ต 0 จนถึงจุดเริ่มต้นของ APK Signing Block)
  2. บล็อกการลงนาม APK
  3. ไดเรกทอรีกลาง ZIP
  4. ZIP สิ้นสุดไดเรกทอรีกลาง

ส่วน APK หลังจากการลงนาม

รูปที่ 2 ส่วน APK หลังจากการลงนาม

APK Signature Scheme v2 ปกป้องความสมบูรณ์ของส่วนที่ 1, 3, 4 และบล็อก signed data ของ APK Signature Scheme v2 Block ที่อยู่ภายในส่วนที่ 2

ความสมบูรณ์ของส่วนที่ 1, 3 และ 4 ได้รับการปกป้องโดยเนื้อหาย่อยตั้งแต่หนึ่งรายการขึ้นไปที่จัดเก็บไว้ในบล็อก signed data ซึ่งได้รับการป้องกันโดยลายเซ็นตั้งแต่หนึ่งรายการขึ้นไป

การสรุปส่วนที่ 1, 3 และ 4 ได้รับการคำนวณดังนี้ คล้ายกับ แผนผัง Merkle สองระดับ แต่ละส่วนจะแบ่งออกเป็นส่วนขนาด 1 MB (2 20 ไบต์) ติดต่อกัน ส่วนสุดท้ายในแต่ละส่วนอาจสั้นกว่า การย่อยของแต่ละอันจะถูกคำนวณผ่านการต่อไบต์ 0xa5 ความยาวของก้อนในหน่วยไบต์ (little-endian uint32) และเนื้อหาของก้อน การแยกย่อยระดับบนสุดจะคำนวณจากการต่อไบต์ 0x5a จำนวนชิ้น (little-endian uint32) และการต่อการแยกย่อยของชิ้นตามลำดับที่ชิ้นปรากฏใน APK การย่อยจะถูกคำนวณแบบเป็นก้อนเพื่อให้สามารถเร่งการคำนวณโดยการทำแบบขนาน

สรุป APK

รูปที่ 3 สรุป APK

การป้องกันส่วนที่ 4 (ZIP End of Central Directory) มีความซับซ้อนโดยส่วนที่มีส่วนออฟเซ็ตของ ZIP Central Directory ค่าชดเชยจะเปลี่ยนไปเมื่อขนาดของ APK Signing Block เปลี่ยนแปลง เช่น เมื่อมีการเพิ่มลายเซ็นใหม่ ดังนั้น เมื่อประมวลผลการแยกย่อยบน ZIP End ของ Central Directory ฟิลด์ที่มีออฟเซ็ตของ ZIP Central Directory จะต้องได้รับการปฏิบัติเหมือนมีออฟเซ็ตของ APK Signing Block

การป้องกันการย้อนกลับ

ผู้โจมตีอาจพยายามให้ APK ที่ลงชื่อ v2 ได้รับการยืนยันว่าเป็น APK ที่ลงชื่อ v1 บนแพลตฟอร์ม Android ที่รองรับการตรวจสอบ APK ที่ลงชื่อ v2 เพื่อบรรเทาการโจมตีนี้ APK ที่ลงนาม v2 และลงนาม v1 จะต้องมีแอตทริบิวต์ X-Android-APK-Signed ในส่วนหลักของไฟล์ META-INF/*.SF ค่าของแอตทริบิวต์คือชุดรหัสรูปแบบลายเซ็น APK ที่คั่นด้วยเครื่องหมายจุลภาค (รหัสของรูปแบบนี้คือ 2) เมื่อตรวจสอบลายเซ็น v1 ผู้ตรวจสอบ APK จะต้องปฏิเสธ APK ที่ไม่มีลายเซ็นสำหรับรูปแบบลายเซ็น APK ที่ผู้ตรวจสอบต้องการจากชุดนี้ (เช่น รูปแบบ v2) การป้องกันนี้ขึ้นอยู่กับข้อเท็จจริงที่ว่าเนื้อหาไฟล์ META-INF/*.SF ได้รับการปกป้องโดยลายเซ็น v1 ดูส่วน การตรวจสอบ APK ที่ลงนามโดย JAR

ผู้โจมตีอาจพยายามดึงลายเซ็นที่แข็งแกร่งกว่าออกจาก APK Signature Scheme v2 Block เพื่อบรรเทาการโจมตีนี้ รายการ ID อัลกอริทึมลายเซ็นที่มีการลงนาม APK จะถูกจัดเก็บไว้ในบล็อก signed data ซึ่งได้รับการปกป้องโดยแต่ละลายเซ็น

การยืนยัน

ใน Android 7.0 และใหม่กว่า สามารถตรวจสอบ APK ตาม APK Signature Scheme v2+ หรือการลงนาม JAR (รูปแบบ v1) แพลตฟอร์มรุ่นเก่าไม่สนใจลายเซ็น v2 และตรวจสอบเฉพาะลายเซ็น v1 เท่านั้น

กระบวนการตรวจสอบลายเซ็น APK

รูปที่ 4 กระบวนการตรวจสอบลายเซ็น APK (ขั้นตอนใหม่เป็นสีแดง)

การตรวจสอบ APK Signature Scheme v2

  1. ค้นหา APK Signing Block และตรวจสอบว่า:
    1. ช่องขนาด 2 ช่องของ APK Signing Block มีค่าเท่ากัน
    2. ZIP Central Directory ตามด้วย ZIP End of Central Directory ทันที
    3. ZIP End of Central Directory ไม่ได้ตามด้วยข้อมูลเพิ่มเติม
  2. ค้นหา APK Signature Scheme v2 Block แรกภายใน APK Signing Block หากมีการบล็อก v2 อยู่ ให้ดำเนินการตามขั้นตอนที่ 3 มิฉะนั้น ให้กลับไปยืนยัน APK โดยใช้รูปแบบ v1
  3. สำหรับ signer แต่ละคนใน APK Signature Scheme v2 Block:
    1. เลือก signature algorithm ID ที่แข็งแกร่งที่สุดที่ได้รับการสนับสนุนจาก signatures การจัดลำดับความแข็งแกร่งนั้นขึ้นอยู่กับการใช้งาน/เวอร์ชันแพลตฟอร์มแต่ละเวอร์ชัน
    2. ตรวจสอบ signature ที่สอดคล้องกันจาก signatures กับ signed data โดยใช้ public key (ขณะนี้สามารถแยกวิเคราะห์ signed data อย่างปลอดภัยแล้ว)
    3. ตรวจสอบว่ารายการเรียงลำดับของ ID อัลกอริทึมลายเซ็นใน digests และ signatures เหมือนกัน (เพื่อป้องกันการลอก/เพิ่มลายเซ็น)
    4. คำนวณการสรุปเนื้อหา APK โดยใช้อัลกอริธึมการสรุปแบบเดียวกับอัลกอริธึมการสรุปที่ใช้โดยอัลกอริธึมลายเซ็น
    5. ตรวจสอบว่าไดเจสต์ที่คำนวณแล้วเหมือนกับ digest ที่สอดคล้องกันจาก digests
    6. ตรวจสอบว่า SubjectPublicKeyInfo ของ certificate certificates ใบแรกเหมือนกับ public key
  4. การยืนยันจะสำเร็จหากพบ signer อย่างน้อยหนึ่งคน และขั้นตอนที่ 3 สำเร็จสำหรับ signer แต่ละคนที่พบ

หมายเหตุ : APK จะต้องไม่ได้รับการยืนยันโดยใช้รูปแบบ v1 หากเกิดความล้มเหลวในขั้นตอนที่ 3 หรือ 4

การตรวจสอบ APK ที่ลงนามโดย JAR (รูปแบบ v1)

APK ที่ลงนามด้วย JAR คือ JAR ที่ลงนามมาตรฐาน ซึ่งจะต้องมีรายการที่ระบุไว้ใน META-INF/MANIFEST.MF ทุกประการ และรายการทั้งหมดจะต้องลงนามโดยผู้ลงนามชุดเดียวกัน ความสมบูรณ์ของมันได้รับการตรวจสอบดังนี้:

  1. ผู้ลงนามแต่ละคนจะแสดงด้วยรายการ META-INF/<signer>.SF และ META-INF/<signer>.(RSA|DSA|EC) JAR
  2. <signer>.(RSA|DSA|EC) คือ PKCS #7 CMS ContentInfo ที่มีโครงสร้าง SignedData ซึ่งมีลายเซ็นต์ที่ได้รับการตรวจสอบยืนยันในไฟล์ <signer>.SF
  3. ไฟล์ <signer>.SF มีไดเจสต์ทั้งไฟล์ของ META-INF/MANIFEST.MF และไดเจสต์ของแต่ละส่วนของ META-INF/MANIFEST.MF การแยกย่อยไฟล์ทั้งหมดของ MANIFEST.MF ได้รับการตรวจสอบแล้ว หากไม่สำเร็จ ระบบจะตรวจสอบการแยกย่อยของแต่ละส่วน MANIFEST.MF แทน
  4. META-INF/MANIFEST.MF ประกอบด้วยส่วนที่มีชื่อที่สอดคล้องกันสำหรับแต่ละรายการ JAR ที่มีการป้องกันความสมบูรณ์ ซึ่งมีส่วนย่อยของเนื้อหาที่ไม่มีการบีบอัดของรายการ การย่อยทั้งหมดเหล่านี้ได้รับการตรวจสอบแล้ว
  5. การตรวจสอบ APK จะล้มเหลวหาก APK มีรายการ JAR ที่ไม่อยู่ใน MANIFEST.MF และไม่ได้เป็นส่วนหนึ่งของลายเซ็น JAR

สายโซ่การป้องกันจึงเป็น <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> เนื้อหาของแต่ละรายการ JAR ที่มีการป้องกันความสมบูรณ์