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 เวอร์ชัน 1 เวอร์ชัน 2 และลายเซ็น APK ที่ใหม่กว่าจะถูกเก็บไว้ใน APK Signing Block ซึ่งเป็นคอนเทนเนอร์ใหม่ที่ได้รับการแนะนำเพื่อรองรับ APK Signature Scheme v2 ในไฟล์ APK นั้น APK Signing Block จะอยู่หน้า ZIP Central Directory ซึ่งอยู่ท้ายไฟล์

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

รูปแบบ

รูปแบบของ APK Signing Block มีดังต่อไปนี้ (ฟิลด์ตัวเลขทั้งหมดเป็นแบบ little-endian):

  • 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 Block

APK ได้รับการลงนามโดยผู้ลงนาม/ข้อมูลประจำตัวตั้งแต่หนึ่งคนขึ้นไป โดยแต่ละรายการแสดงด้วยคีย์การลงนาม ข้อมูลนี้ถูกจัดเก็บเป็น 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, เกลือ 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:

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

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

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

  1. เนื้อหาของรายการ ZIP (ตั้งแต่ออฟเซ็ต 0 จนถึงจุดเริ่มต้นของ APK Signing Block)
  2. บล็อกการลงชื่อ APK
  3. ZIP Central Directory
  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 tree สองระดับ แต่ละส่วนจะถูกแบ่งออกเป็น 1 MB (2 20 ไบต์) ต่อเนื่องกัน ชิ้นสุดท้ายในแต่ละส่วนอาจสั้นลง ไดเจสต์ของแต่ละอันจะถูกคำนวณจากการต่อกันของ byte 0xa5 , ความยาวของอันเป็นไบต์ (little-endian uint32) และเนื้อหาของส่วน ไดเจสต์ระดับบนสุดคำนวณจากการต่อข้อมูลของ byte 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 เพื่อลดการโจมตีนี้ รายการรหัสอัลกอริธึมลายเซ็นที่มีการลงนาม 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. ฟิลด์ขนาดสองช่องของ 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 scheme
  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. ผู้ลงนามแต่ละคนจะแสดงด้วยรายการ JAR ของ META-INF/<signer>.SF และ META-INF/<signer>.(RSA|DSA|EC)
  2. <signer>.(RSA|DSA|EC) คือ PKCS #7 CMS ContentInfo ที่มีโครงสร้าง SignedData ซึ่งมีการตรวจสอบลายเซ็นบนไฟล์ <signer>.SF
  3. <ผู้ลงนาม>ไฟล์ .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 ที่มีการป้องกันความสมบูรณ์