APK Signature Scheme v3

Android 9 รองรับ การหมุนเวียนคีย์ APK ซึ่งทำให้แอปสามารถเปลี่ยนคีย์การลงนามโดยเป็นส่วนหนึ่งของการอัปเดต APK เพื่อให้การหมุนเวียนใช้งานได้จริง APK จะต้องระบุระดับความน่าเชื่อถือระหว่างคีย์การลงนามใหม่และเก่า เพื่อรองรับการหมุนเวียนคีย์ เราได้อัปเดต รูปแบบลายเซ็น APK จาก v2 เป็น v3 เพื่ออนุญาตให้ใช้คีย์ใหม่และเก่าได้ V3 เพิ่มข้อมูลเกี่ยวกับเวอร์ชัน SDK ที่รองรับและโครงสร้างการพิสูจน์การหมุนเวียนให้กับบล็อกการลงนาม APK

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

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

รูปแบบ APK Signing Block v3 เหมือนกับ v2 ลายเซ็น v3 ของ APK จะถูกจัดเก็บเป็นคู่ค่า ID ที่มี ID 0xf05368c0

APK Signature Scheme v3 บล็อก

รูปแบบ v3 ได้รับการออกแบบให้คล้ายกับ รูปแบบ v2 มาก มีรูปแบบทั่วไปเหมือนกันและรองรับ ID อัลกอริธึมลายเซ็น ขนาดคีย์ และเส้นโค้ง EC เดียวกัน

อย่างไรก็ตาม รูปแบบ v3 จะเพิ่มข้อมูลเกี่ยวกับเวอร์ชัน SDK ที่รองรับและโครงสร้างการพิสูจน์การหมุนเวียน

รูปแบบ

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

รูปแบบของ APK Signature Scheme v3 Block เป็นไปตามรูปแบบ v2:

  • ลำดับคำนำหน้าความยาวของ signer ที่นำหน้าความยาว :
    • signed data หน้าด้วยความยาว :
      • ลำดับที่นำหน้าความยาวของ digests ที่นำหน้าด้วยความยาว :
        • signature algorithm ID (4 ไบต์)
        • digest (คำนำหน้าความยาว)
      • ลำดับความยาวนำหน้าของ certificates X.509 :
        • certificate X.509 ที่นำหน้าความยาว (แบบฟอร์ม ASN.1 DER)
      • minSDK (uint32) - ผู้ลงนามนี้ควรถูกละเว้นหากเวอร์ชันแพลตฟอร์มต่ำกว่าตัวเลขนี้
      • maxSDK (uint32) - ผู้ลงนามนี้ควรละเว้นหากเวอร์ชันแพลตฟอร์มสูงกว่าตัวเลขนี้
      • ลำดับความยาวนำ additional attributes ที่นำหน้าความยาว:
        • ID (uint32)
        • value (ความยาวตัวแปร: ความยาวของแอตทริบิวต์เพิ่มเติม - 4 ไบต์)
        • ID - 0x3ba06f8c
        • value - โครงสร้างการพิสูจน์การหมุน
    • minSDK (uint32) - ซ้ำกับค่า minSDK ในส่วนข้อมูลที่ลงนาม - ใช้เพื่อข้ามการตรวจสอบลายเซ็นนี้หากแพลตฟอร์มปัจจุบันไม่อยู่ในช่วง ต้องตรงกับค่าข้อมูลที่ลงนาม
    • maxSDK (uint32) - ซ้ำกับค่า maxSDK ในส่วนข้อมูลที่ลงนาม - ใช้เพื่อข้ามการตรวจสอบลายเซ็นนี้หากแพลตฟอร์มปัจจุบันไม่อยู่ในช่วง ต้องตรงกับค่าข้อมูลที่ลงนาม
    • ลำดับความยาวนำหน้าของ signatures ที่นำหน้าความยาว :
      • signature algorithm ID (uint32)
      • signature ที่มีความยาวนำหน้าเหนือ signed data
    • public key ที่มีความยาวนำหน้า (SubjectPublicKeyInfo, แบบฟอร์ม ASN.1 DER)

โครงสร้างใบรับรองการหมุนเวียนและใบรับรองเก่าที่เชื่อถือได้ในตัวเอง

โครงสร้างการพิสูจน์การหมุนเวียนช่วยให้แอปหมุนเวียนใบรับรองการลงนามโดยไม่ถูกบล็อกในแอปอื่นๆ ที่พวกเขาสื่อสารด้วย เพื่อให้บรรลุเป้าหมายนี้ ลายเซ็นของแอปประกอบด้วยข้อมูลใหม่ 2 ชิ้น:

  • การยืนยันสำหรับบุคคลที่สามว่าใบรับรองการลงนามของแอปสามารถเชื่อถือได้ทุกที่ที่แอปรุ่นก่อนได้รับความเชื่อถือ
  • ใบรับรองการลงนามเก่าของแอปซึ่งตัวแอปเองยังคงเชื่อถือ

แอตทริบิวต์ Proof-of-rotation ในส่วนข้อมูลที่ลงนามประกอบด้วยรายการที่ลิงก์เดี่ยว โดยแต่ละโหนดจะมีใบรับรองการลงนามซึ่งใช้ในการลงนามแอปเวอร์ชันก่อนหน้า แอ็ตทริบิวต์นี้มีไว้เพื่อให้มีโครงสร้างข้อมูล proof-of-rotation และ self-trusted-old-certs รายการจะเรียงลำดับตามเวอร์ชันที่มีใบรับรองการลงนามที่เก่าที่สุดซึ่งสอดคล้องกับโหนดรูท โครงสร้างข้อมูลพิสูจน์การหมุนเวียนถูกสร้างขึ้นโดยให้ใบรับรองในแต่ละโหนดลงชื่อถัดไปในรายการ และด้วยเหตุนี้จึงเพิ่มแต่ละคีย์ใหม่ด้วยหลักฐานว่าควรเชื่อถือได้เท่ากับคีย์เก่า

โครงสร้างข้อมูลใบรับรองเก่าที่เชื่อถือได้ในตัวเองถูกสร้างขึ้นโดยการเพิ่มแฟล็กให้กับแต่ละโหนดเพื่อระบุความเป็นสมาชิกและคุณสมบัติในชุด ตัวอย่างเช่น อาจมีธงปรากฏขึ้นเพื่อระบุว่าใบรับรองการลงนามที่โหนดที่กำหนดนั้นเชื่อถือได้ในการรับสิทธิ์ลายเซ็น Android การตั้งค่าสถานะนี้อนุญาตให้แอปอื่นๆ ที่ลงนามโดยใบรับรองเก่ายังคงได้รับสิทธิ์ในการลงนามที่กำหนดโดยแอปที่ลงนามด้วยใบรับรองการลงนามใหม่ เนื่องจากแอตทริบิวต์ Proof-of-Rotation ทั้งหมดอยู่ในส่วนข้อมูลที่ลงนามของฟิลด์ signer v3 จึงได้รับการปกป้องโดยคีย์ที่ใช้ในการลงนาม APK ที่มี

รูปแบบนี้ป้องกันไม่ให้ คีย์การลงนามหลายคีย์ และการบรรจบกันของ ใบรับรองการลงนามของบรรพบุรุษที่แตกต่างกัน ให้เป็นหนึ่งเดียว (โหนดเริ่มต้นหลายโหนดไปยังซิงก์ทั่วไป)

รูปแบบ

หลักฐานการหมุนเวียนจะถูกจัดเก็บไว้ใน APK Signature Scheme v3 Block ภายใต้ ID 0x3ba06f8c รูปแบบคือ:

  • ลำดับความยาวนำหน้าของ levels ความยาวนำหน้า:
    • signed data หน้าความยาว (ตามใบรับรองก่อนหน้า - ถ้ามี)
      • certificate X.509 ที่นำหน้าความยาว (แบบฟอร์ม ASN.1 DER)
      • signature algorithm ID (uint32) - อัลกอริธึมที่ใช้โดยใบรับรองในระดับก่อนหน้า
    • flags (uint32) - แฟล็กที่ระบุว่าใบรับรองนี้ควรอยู่ในโครงสร้างใบรับรองแบบเก่าที่น่าเชื่อถือในตัวเองหรือไม่ และสำหรับการดำเนินการใด
    • signature algorithm ID (uint32) - ต้องตรงกับรหัสจากส่วนข้อมูลที่ลงนามในระดับถัดไป
    • signature ที่มีความยาวนำหน้าเหนือ signed data ข้างต้น

ใบรับรองหลายใบ

ปัจจุบัน Android ถือว่า APK ที่ลงนามด้วยใบรับรองหลายใบนั้นมีข้อมูลประจำตัวการลงนามที่ไม่ซ้ำกันแยกจากใบรับรองที่ประกอบด้วย ดังนั้น แอ็ตทริบิวต์ Proof-of-rotation ในส่วนข้อมูลที่ลงนามจะสร้างกราฟอะไซคลิกโดยตรง ซึ่งสามารถมองได้ดีกว่าเป็นรายการที่ลิงก์เดี่ยว โดยแต่ละชุดของผู้ลงนามสำหรับเวอร์ชันที่กำหนดจะแสดงเป็นหนึ่งโหนด สิ่งนี้จะเพิ่มความซับซ้อนเป็นพิเศษให้กับโครงสร้างการพิสูจน์การหมุนเวียน (เวอร์ชันที่มีผู้ลงนามหลายรายด้านล่าง) โดยเฉพาะการสั่งซื้อกลายเป็นเรื่องกังวล ยิ่งไปกว่านั้น ยังไม่สามารถลงนาม APK ได้อย่างอิสระอีกต่อไป เนื่องจากโครงสร้างการพิสูจน์การหมุนเวียนต้องมีใบรับรองการลงนามเก่าที่ลงนามใบรับรองชุดใหม่ แทนที่จะลงนามทีละใบรับรอง ตัวอย่างเช่น APK ที่ลงนามโดยคีย์ A ที่ต้องการลงนามโดยคีย์ใหม่ B และ C สองคีย์ไม่สามารถให้ผู้ลงนาม B เพียงรวมลายเซ็นโดย A หรือ B ได้ เนื่องจากนั่นเป็นข้อมูลประจำตัวของการลงนามที่แตกต่างจาก B และ C สิ่งนี้จะ หมายความว่าผู้ลงนามจะต้องประสานงานก่อนที่จะสร้างโครงสร้างดังกล่าว

แอตทริบิวต์หลักฐานการหมุนเวียนผู้ลงนามหลายราย

  • ลำดับความยาวนำหน้าของ sets คำนำหน้าความยาว :
    • signed data (ตามชุดก่อนหน้า - ถ้ามี)
      • ลำดับ certificates ที่นำหน้าความยาว
        • certificate X.509 ที่นำหน้าความยาว (แบบฟอร์ม ASN.1 DER)
      • ลำดับของ signature algorithm IDs (uint32) - หนึ่งรหัสสำหรับแต่ละใบรับรองจากชุดก่อนหน้าในลำดับเดียวกัน
    • flags (uint32) - แฟล็กที่ระบุว่าชุดใบรับรองนี้ควรอยู่ในโครงสร้างใบรับรองแบบเก่าที่น่าเชื่อถือในตัวเองหรือไม่ และสำหรับการดำเนินการใด
    • ลำดับความยาวนำหน้าของ signatures ที่นำหน้าความยาว :
      • signature algorithm ID (uint32) - ต้องตรงกับรหัสจากส่วนข้อมูลที่ลงนาม
      • signature ที่มีความยาวนำหน้าเหนือ signed data ข้างต้น

บรรพบุรุษหลายรายในโครงสร้างการพิสูจน์การหมุนเวียน

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

การยืนยัน

ใน Android 9 ขึ้นไป สามารถตรวจสอบ APK ได้ตาม APK Signature Scheme v3, v2 Scheme หรือ V1 Scheme แพลตฟอร์มรุ่นเก่าไม่สนใจลายเซ็น v3 และพยายามตรวจสอบลายเซ็น v2 จากนั้นจึง v1

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

รูปที่ 1 กระบวนการตรวจสอบลายเซ็น APK

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

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

การตรวจสอบ

หากต้องการทดสอบว่าอุปกรณ์ของคุณรองรับ v3 อย่างถูกต้อง ให้รันการทดสอบ PkgInstallSignatureVerificationTest.java CTS ใน cts/hostsidetests/appsecurity/src/android/appsecurity/cts/