APK Signature Scheme v2

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

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

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 ที่มีคํานําหน้าความยาว 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 ระบบจะจัดเก็บข้อมูลต่อไปนี้สำหรับผู้ลงนามแต่ละราย

  • (อัลกอริทึมลายเซ็น ไดเจสต์ ลายเซ็น) tuples ระบบจะจัดเก็บข้อมูลสรุปไว้เพื่อแยกการยืนยันลายเซ็นออกจากการตรวจสอบความสมบูรณ์ของเนื้อหา 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, Salt 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 Signature Scheme v2 ปกป้องความสมบูรณ์ของส่วนที่ 1, 3, 4 และบล็อก signed data ของบล็อก APK Signature Scheme 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 Signature Scheme v2 ขึ้นไปหรือการรับรอง JAR (รูปแบบ v1) แพลตฟอร์มเก่าจะไม่สนใจลายเซ็น v2 และจะยืนยันเฉพาะลายเซ็น v1

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

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

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

  1. ค้นหาบล็อกการรับรอง APK และตรวจสอบสิ่งต่อไปนี้
    1. ช่องขนาด 2 ช่องของบล็อกการรับรอง APK มีค่าเดียวกัน
    2. ZIP Central Directory ตามด้วยระเบียนไดเรกทอรีสิ้นสุดของ ZIP ใน ZIP
    3. ไฟล์ ZIP ของ Central Directory ไม่ตามด้วยข้อมูลเพิ่มเติม
  2. ค้นหาบล็อก APK Signature Scheme v2 แรกภายในบล็อกการรับรอง APK หากมีบล็อก v2 ให้ไปยังขั้นตอนที่ 3 มิฉะนั้น ให้เปลี่ยนไปยืนยัน APKโดยใช้รูปแบบ v1
  3. สำหรับ signer แต่ละรายการในบล็อก Signature Scheme v2 ของ APK
    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 ที่ได้รับการคุ้มครองความสมบูรณ์แต่ละรายการ