APK Signature Scheme v2

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

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

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

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

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

บล็อกการรับรอง APK

ระบบจะจัดเก็บลายเซ็น APK เวอร์ชัน 2 ขึ้นไปไว้ในบล็อกการรับรอง APK ซึ่งเป็นคอนเทนเนอร์ใหม่ที่เปิดตัวเพื่อรองรับ APK Signature Scheme v2 เพื่อรักษาความสามารถในการใช้งานร่วมกันย้อนหลังกับรูปแบบ APK เวอร์ชัน 1 ในไฟล์ APK บล็อกการรับรอง APK จะอยู่ก่อนไดเรกทอรีกลาง ZIP ซึ่งอยู่ท้ายไฟล์

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

รูปแบบ

รูปแบบของบล็อกการรับรอง APK มีดังนี้ (ช่องตัวเลขทั้งหมดเป็นแบบ Little-endian)

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

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

ระบบจะไม่สนใจคู่รหัส-ค่าที่มีรหัสที่ไม่รู้จักเมื่อตีความบล็อก

บล็อก APK Signature Scheme v2

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

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

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

รูปแบบ

บล็อก APK Signature Scheme v2 จะจัดเก็บไว้ในบล็อกการรับรอง APK ภายใต้รหัส 0x7109871a

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

  • ลำดับที่มีความยาวนำหน้าของ signer ที่มีความยาวนำหน้า ดังนี้
    • signed data ที่มีความยาวนำหน้า:
      • ลำดับที่มีความยาวนำหน้าของ digests ที่มีความยาวนำหน้า ดังนี้
      • ลำดับ X.509 ที่มีความยาวนำหน้า certificates ดังนี้
        • X.509 certificate ที่มีความยาวนำหน้า (รูปแบบ 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, เกลือ 32 ไบต์, ข้อมูลสรุป: 0xbc
  • 0x0102 - RSASSA-PSS พร้อมข้อมูลสรุป SHA2-512, SHA2-512 MGF1, เกลือ 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 ที่รองรับ:

  • RSA: 1024, 2048, 4096, 8192, 16384
  • EC: NIST P-256, P-384, P-521
  • DSA: 1024, 2048, 3072

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

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

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

ส่วนต่างๆ ของ APK หลังจากการรับรอง

รูปที่ 2 ส่วนต่างๆ ของ APK หลังจากการรับรอง

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

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

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

ข้อมูลสรุป APK

รูปที่ 3 ข้อมูลสรุป APK

การปกป้องส่วนที่ 4 (ส่วนสิ้นสุดของไดเรกทอรีส่วนกลางของ ZIP) มีความซับซ้อนเนื่องจากส่วนนี้มีออฟเซตของไดเรกทอรีส่วนกลางของ ZIP ออฟเซตจะเปลี่ยนแปลงเมื่อขนาดของบล็อกการรับรอง APK เปลี่ยนแปลง เช่น เมื่อเพิ่มลายเซ็นใหม่ ดังนั้น เมื่อคํานวณข้อมูลสรุปจากส่วน "End of Central Directory" ของ ZIP จะต้องถือว่าช่องที่มีออฟเซตของ ZIP Central Directory มีออฟเซตของบล็อกการรับรอง APK

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

ผู้โจมตีอาจพยายามยืนยัน 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 เพื่อลดการโจมตีนี้ รายการรหัสอัลกอริทึมลายเซ็นที่ใช้รับรอง APK จะจัดเก็บไว้ในบล็อก signed data ซึ่งได้รับการปกป้องโดยลายเซ็นแต่ละรายการ

การยืนยัน

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

กระบวนการยืนยันลายเซ็น APK

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

การยืนยัน APK Signature Scheme v2

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