Lược đồ chữ ký APK v2

Lược đồ chữ ký APK v2 là lược đồ chữ ký toàn tệp giúp tăng tốc độ xác minh và tăng cường đảm bảo tính toàn vẹn bằng cách phát hiện mọi thay đổi đối với các phần được bảo vệ của APK.

Ký bằng Lược đồ chữ ký APK v2 sẽ chèn Khối ký APK vào tệp APK ngay trước phần Thư mục trung tâm ZIP. Bên trong Khối ký APK, chữ ký v2 và thông tin nhận dạng người ký được lưu trữ trong Khối sơ đồ chữ ký APK v2 .

APK trước và sau khi ký

Hình 1. APK trước và sau khi ký

APK Signature Lược đồ v2 đã được giới thiệu trong Android 7.0 (Nougat). Để làm cho APK có thể cài đặt được trên Android 6.0 (Marshmallow) và các thiết bị cũ hơn, APK phải được ký bằng cách sử dụng ký JAR trước khi được ký bằng lược đồ v2.

Khối ký APK

Để duy trì khả năng tương thích ngược với định dạng APK v1, chữ ký APK v2 và mới hơn được lưu trữ bên trong Khối ký APK, một vùng chứa mới được giới thiệu để hỗ trợ Lược đồ chữ ký APK v2. Trong tệp APK, Khối ký APK nằm ngay trước Thư mục trung tâm ZIP, nằm ở cuối tệp.

Khối chứa các cặp giá trị ID được bao bọc theo cách giúp định vị khối trong APK dễ dàng hơn. Chữ ký v2 của APK được lưu trữ dưới dạng cặp giá trị ID có ID 0x7109871a.

Định dạng

Định dạng của Khối ký APK như sau (tất cả các trường số đều là endian nhỏ):

  • size of block bằng byte (không bao gồm trường này) (uint64)
  • Trình tự các cặp giá trị ID có tiền tố uint64:
    • ID (uint32)
    • value (độ dài thay đổi: độ dài của cặp - 4 byte)
  • size of block bằng byte—giống như trường đầu tiên (uint64)
  • magic “APK Sig Block 42” (16 byte)

APK được phân tích cú pháp bằng cách trước tiên tìm phần đầu của Thư mục Trung tâm ZIP (bằng cách tìm bản ghi ZIP Cuối của Thư mục Trung tâm ở cuối tệp, sau đó đọc phần bù bắt đầu của Thư mục Trung tâm từ bản ghi). Giá trị magic cung cấp một cách nhanh chóng để xác định rằng những gì có trước Thư mục Trung tâm có thể là Khối ký APK. size of block sau đó sẽ trỏ đến điểm bắt đầu của khối trong tệp một cách hiệu quả.

Các cặp giá trị ID có ID không xác định sẽ bị bỏ qua khi diễn giải khối.

Khối chữ ký APK v2

APK được ký bởi một hoặc nhiều người ký/danh tính, mỗi người được biểu thị bằng một khóa ký. Thông tin này được lưu trữ dưới dạng Khối sơ đồ chữ ký APK v2. Đối với mỗi người ký, thông tin sau được lưu trữ:

  • (thuật toán chữ ký, tóm tắt, chữ ký) tuple. Thông báo được lưu trữ để tách xác minh chữ ký khỏi kiểm tra tính toàn vẹn của nội dung APK.
  • Chuỗi chứng chỉ X.509 đại diện cho danh tính của người ký.
  • Các thuộc tính bổ sung dưới dạng cặp khóa-giá trị.

Đối với mỗi người ký, APK được xác minh bằng chữ ký được hỗ trợ từ danh sách được cung cấp. Chữ ký có thuật toán chữ ký không xác định sẽ bị bỏ qua. Tùy thuộc vào mỗi lần triển khai để chọn chữ ký nào sẽ sử dụng khi gặp phải nhiều chữ ký được hỗ trợ. Điều này cho phép giới thiệu các phương thức ký mạnh mẽ hơn trong tương lai theo cách tương thích ngược. Cách tiếp cận được đề xuất là xác minh chữ ký mạnh nhất.

Định dạng

Khối chữ ký APK v2 được lưu trữ bên trong Khối ký APK dưới ID 0x7109871a .

Định dạng của Khối Lược đồ Chữ ký APK v2 như sau (tất cả các giá trị số đều là endian nhỏ, tất cả các trường có tiền tố độ dài đều sử dụng uint32 cho độ dài):

  • chuỗi có tiền tố độ dài của signer có tiền tố độ dài:
    • signed data có tiền tố dài:
      • chuỗi có tiền tố độ dài của digests có tiền tố độ dài:
      • chuỗi tiền tố có độ dài của certificates X.509:
        • certificate chỉ X.509 có tiền tố dài (mẫu ASN.1 DER)
      • chuỗi có tiền tố độ dài của additional attributes độ dài:
        • ID (uint32)
        • value (độ dài thay đổi: độ dài của thuộc tính bổ sung - 4 byte)
    • chuỗi có tiền tố độ dài của signatures có tiền tố độ dài:
      • signature algorithm ID (uint32)
      • signature có tiền tố dài trên signed data
    • public key độ dài (SubjectPublicKeyInfo, mẫu ASN.1 DER)

ID thuật toán chữ ký

  • 0x0101—RSASSA-PSS với thông báo SHA2-256, SHA2-256 MGF1, 32 byte muối, đoạn giới thiệu: 0xbc
  • 0x0102—RSASSA-PSS với thông báo SHA2-512, SHA2-512 MGF1, 64 byte muối, đoạn giới thiệu: 0xbc
  • 0x0103—RSASSA-PKCS1-v1_5 với thông báo SHA2-256. Điều này dành cho các hệ thống xây dựng yêu cầu chữ ký xác định.
  • 0x0104—RSASSA-PKCS1-v1_5 với thông báo SHA2-512. Điều này dành cho các hệ thống xây dựng yêu cầu chữ ký xác định.
  • 0x0201—ECDSA với thông báo SHA2-256
  • 0x0202—ECDSA với thông báo SHA2-512
  • 0x0301—DSA với thông báo SHA2-256

Tất cả các thuật toán chữ ký trên đều được nền tảng Android hỗ trợ. Các công cụ ký tên có thể hỗ trợ một tập hợp con các thuật toán.

Kích thước phím được hỗ trợ và đường cong EC:

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

Nội dung được bảo vệ toàn vẹn

Với mục đích bảo vệ nội dung APK, APK bao gồm bốn phần:

  1. Nội dung của các mục ZIP (từ offset 0 cho đến khi bắt đầu Khối ký APK)
  2. Khối ký APK
  3. Thư mục trung tâm ZIP
  4. ZIP Cuối thư mục trung tâm

Phần APK sau khi ký

Hình 2. Các phần APK sau khi ký

Lược đồ chữ ký APK v2 bảo vệ tính toàn vẹn của các phần 1, 3, 4 và các khối signed data của Khối Lược đồ chữ ký APK v2 có trong phần 2.

Tính toàn vẹn của các phần 1, 3 và 4 được bảo vệ bởi một hoặc nhiều bản tóm tắt nội dung của chúng được lưu trữ trong các khối signed data , sau đó được bảo vệ bởi một hoặc nhiều chữ ký.

Việc tóm tắt các phần 1, 3 và 4 được tính toán như sau, tương tự như cây Merkle hai cấp. Mỗi phần được chia thành các khối 1 MB (2 20 byte) liên tiếp. Đoạn cuối cùng trong mỗi phần có thể ngắn hơn. Thông báo của từng đoạn được tính toán dựa trên cách nối byte 0xa5 , độ dài của đoạn tính bằng byte (uint32 endian nhỏ) và nội dung của đoạn đó. Thông báo tóm tắt cấp cao nhất được tính toán dựa trên cách ghép byte 0x5a , số lượng khối (uint32 endian nhỏ) và cách nối các thông báo tóm tắt của các khối theo thứ tự các khối xuất hiện trong APK. Thông báo được tính toán theo kiểu phân đoạn để tăng tốc độ tính toán bằng cách song song hóa nó.

Thông báo APK

Hình 3. Thông báo APK

Việc bảo vệ phần 4 (ZIP End of Central Directory) rất phức tạp bởi phần chứa offset của ZIP Central Directory. Phần bù thay đổi khi kích thước của Khối ký APK thay đổi, chẳng hạn như khi chữ ký mới được thêm vào. Do đó, khi tính toán thông báo trên ZIP Cuối Thư mục Trung tâm, trường chứa phần bù của Thư mục Trung tâm ZIP phải được coi là chứa phần bù của Khối ký APK.

Bảo vệ khôi phục

Kẻ tấn công có thể cố gắng xác minh APK có chữ ký v2 dưới dạng APK có chữ ký v1 trên nền tảng Android hỗ trợ xác minh APK có chữ ký v2. Để giảm thiểu cuộc tấn công này, các APK có chữ ký v2 cũng có chữ ký v1 phải chứa thuộc tính X-Android-APK-Signed trong phần chính của tệp META-INF/*.SF. Giá trị của thuộc tính là tập hợp ID lược đồ chữ ký APK được phân tách bằng dấu phẩy (ID của lược đồ này là 2). Khi xác minh chữ ký v1, người xác minh APK phải từ chối các APK không có chữ ký cho sơ đồ chữ ký APK mà người xác minh ưu tiên từ bộ này (ví dụ: sơ đồ v2). Việc bảo vệ này dựa trên thực tế là nội dung các tệp META-INF/*.SF được bảo vệ bằng chữ ký v1. Xem phần về xác minh APK có chữ ký JAR .

Kẻ tấn công có thể cố gắng loại bỏ các chữ ký mạnh hơn khỏi Khối Lược đồ Chữ ký APK v2. Để giảm thiểu cuộc tấn công này, danh sách ID thuật toán chữ ký mà APK đang được ký sẽ được lưu trữ trong khối signed data và được bảo vệ bằng mỗi chữ ký.

xác minh

Trong Android 7.0 trở lên, APK có thể được xác minh theo Lược đồ chữ ký APK v2+ hoặc ký JAR (sơ đồ v1). Các nền tảng cũ hơn bỏ qua chữ ký v2 và chỉ xác minh chữ ký v1.

Quá trình xác minh chữ ký APK

Hình 4. Quy trình xác minh chữ ký APK (các bước mới màu đỏ)

Xác minh sơ đồ chữ ký APK v2

  1. Xác định vị trí Khối ký APK và xác minh rằng:
    1. Hai trường kích thước của Khối ký APK chứa cùng một giá trị.
    2. Ngay sau Thư mục Trung tâm ZIP là bản ghi Cuối thư mục Trung tâm ZIP.
    3. ZIP Cuối thư mục trung tâm không được theo sau bởi nhiều dữ liệu hơn.
  2. Xác định vị trí Khối sơ đồ chữ ký APK v2 đầu tiên bên trong Khối ký APK. Nếu có Khối v2, hãy chuyển sang bước 3. Nếu không, hãy quay lại xác minh APK bằng lược đồ v1 .
  3. Đối với mỗi signer trong Khối Sơ đồ Chữ ký APK v2:
    1. Chọn signature algorithm ID được hỗ trợ mạnh nhất từ signatures . Thứ tự cường độ tùy thuộc vào từng phiên bản triển khai/nền tảng.
    2. Xác minh signature tương ứng từ signatures đối với signed data bằng public key . (Bây giờ việc phân tích signed data là an toàn.)
    3. Xác minh rằng danh sách thứ tự các ID thuật toán chữ ký trong digestssignatures là giống hệt nhau. (Điều này nhằm ngăn chặn việc tước/bổ sung chữ ký.)
    4. Tính toán thông báo tóm tắt nội dung APK bằng cách sử dụng thuật toán tóm tắt giống như thuật toán tóm tắt được thuật toán chữ ký sử dụng.
    5. Xác minh rằng thông báo được tính toán giống hệt với digest tương ứng từ digests .
    6. Xác minh rằng Chủ đềPublicKeyInfo của certificate certificates đầu tiên giống hệt với public key .
  4. Việc xác minh thành công nếu tìm thấy ít nhất một signer và bước 3 thành công đối với mỗi signer được tìm thấy.

Lưu ý : Không được xác minh APK bằng sơ đồ v1 nếu xảy ra lỗi ở bước 3 hoặc 4.

Xác minh APK có chữ ký JAR (sơ đồ v1)

APK có chữ ký JAR là một JAR có chữ ký tiêu chuẩn , phải chứa chính xác các mục được liệt kê trong META-INF/MANIFEST.MF và trong đó tất cả các mục nhập phải được ký bởi cùng một nhóm người ký. Tính toàn vẹn của nó được xác minh như sau:

  1. Mỗi người ký được đại diện bởi một mục nhập JAR META-INF/<signer>.SF và META-INF/<signer>.(RSA|DSA|EC).
  2. <signer>.(RSA|DSA|EC) là PKCS #7 CMS ContentInfo có cấu trúc SignedData có chữ ký được xác minh qua tệp <signer>.SF.
  3. Tệp <signer>.SF chứa bản tóm tắt toàn bộ tệp của META-INF/MANIFEST.MF và bản tóm tắt của từng phần của META-INF/MANIFEST.MF. Bản tóm tắt toàn bộ tệp của MANIFEST.MF đã được xác minh. Nếu không thành công, thay vào đó, bản tóm tắt của từng phần MANIFEST.MF sẽ được xác minh.
  4. META-INF/MANIFEST.MF chứa, đối với mỗi mục nhập JAR được bảo vệ toàn vẹn, một phần được đặt tên tương ứng chứa bản tóm tắt nội dung không nén của mục nhập. Tất cả những thông báo này đã được xác minh.
  5. Xác minh APK không thành công nếu APK chứa các mục nhập JAR không được liệt kê trong MANIFEST.MF và không phải là một phần của chữ ký JAR.

Do đó, chuỗi bảo vệ là <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> nội dung của mỗi mục nhập JAR được bảo vệ toàn vẹn.