Sơ đồ chữ ký APK v2 là một sơ đồ chữ ký toàn bộ 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 bất kỳ thay đổi nào đối với các phần được bảo vệ của APK.
Việc ký bằng APK Signature Scheme v2 sẽ chèn một APK Signing Block vào tệp APK ngay trước phần ZIP Central Directory. 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 ký kết APK v2 .
Hình 1. APK trước và sau khi ký
APK Signature Scheme v2 đã được giới thiệu trong Android 7.0 (Nougat). Để làm cho APK có thể cài đặt trên Android 6.0 (Marshmallow) và các thiết bị cũ hơn, APK đó phải được ký bằng cách 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ợ Sơ đồ 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 với 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à little-endian):
-
size of block
bằng byte (không bao gồm trường này) (uint64) - Chuỗi 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 tìm đầu tiên của Thư mục Trung tâm ZIP (bằng cách tìm bản ghi ZIP Cuối 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 để thiết lập rằng những gì đứng trước Thư mục trung tâm có khả năng là Khối ký APK. size of block
sau đó trỏ đến đầu 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 nên được bỏ qua khi diễn giải khối.
Lược đồ chữ ký APK v2 Block
APK được ký bởi một hoặc nhiều người ký / danh tính, mỗi người được đại diện bởi một khóa ký. Thông tin này được lưu trữ dưới dạng khối APK Signature Scheme v2. Đối với mỗi người ký, thông tin sau được lưu trữ:
- (thuật toán chữ ký, thông báo, chữ ký) bộ giá trị. 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ý với các thuật toán chữ ký không xác định bị bỏ qua. Tùy thuộc vào mỗi triển khai để chọn chữ ký nào sẽ sử dụng khi gặp nhiều chữ ký được hỗ trợ. Điều này cho phép giới thiệu các phương pháp ký mạnh 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
Gói chữ ký APK v2 Khối được lưu trữ bên trong Khối ký APK dưới ID 0x7109871a
.
Định dạng của APK Signature Scheme v2 Block như sau (tất cả các giá trị số đều là little-endian, tất cả các trường có tiền tố độ dài sử dụng uint32 cho độ dài):
- trình tự có tiền tố độ dài của
signer
có tiền tố độ dài:-
signed data
có tiền tố độ dài:- trình tự có tiền tố độ dài của các thông báo có tiền tố độ
digests
:-
signature algorithm ID
(uint32) - Thông báo (có tiền tố độ dài)
digest
Nội dung được bảo vệ toàn vẹn
-
- trình tự có tiền tố độ dài của chứng chỉ
certificates
:- chứng chỉ
certificate
có tiền tố độ dài (mẫu ASN.1 DER)
- chứng chỉ
- chuỗi các
additional attributes
có tiền tố chiều dài:-
ID
(uint32) -
value
(biến-độ dài: độ dài của thuộc tính bổ sung - 4 byte)
-
- trình tự có tiền tố độ dài của các thông báo có tiền tố độ
- trình tự có tiền tố độ dài của
signatures
tiền tố độ dài:-
signature algorithm ID
(uint32) -
signature
tiền tố độ dài trênsigned data
-
-
public key
có tiền tố độ dài (SubjectPublicKeyInfo, biểu 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, trailer: 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 hỗ trợ bởi nền tảng Android. Các công cụ ký có thể hỗ trợ một tập hợp con của 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:
- Nội dung của các mục ZIP (từ độ lệch 0 cho đến khi bắt đầu Khối ký APK)
- Khối ký APK
- Thư mục trung tâm ZIP
- ZIP Cuối Thư mục Trung tâm
Hình 2. Các phần APK sau khi ký
Sơ đồ 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 Sơ đồ chữ ký APK v2 Khối có bên 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 phân tích nội dung của chúng được lưu trữ trong các khối signed data
, đến lượt nó, được bảo vệ bằng một hoặc nhiều chữ ký.
Thông báo trên các phần 1, 3 và 4 được tính như sau, tương tự như cây Merkle hai cấp. Mỗi phần được chia thành các phần 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. Bản tóm tắt của mỗi đoạn được tính dựa trên việc ghép byte 0xa5
, độ dài của đoạn tính bằng byte (little-endian uint32) và nội dung của đoạn này. Thông báo cấp cao nhất được tính toán dựa trên việc ghép byte 0x5a
, số lượng các phần (little-endian uint32) và nối các thông báo của các phần theo thứ tự các phần xuất hiện trong APK. Thông báo được tính toán theo kiểu phân khúc để cho phép tăng tốc độ tính toán bằng cách song song hóa nó.
Hình 3. Thông báo APK
Việc bảo vệ phần 4 (ZIP End of Central Directory) phức tạp bởi phần chứa phần bù của ZIP Central Directory. Ví dụ: bù đắp thay đổi khi kích thước của Khối ký APK thay đổi khi một chữ ký mới được thêm vào. Do đó, khi thông báo điện toán qua ZIP End of Central Directory, trường chứa phần bù của ZIP Central Directory 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 ký v2 là APK ký v1 trên nền tảng Android hỗ trợ xác minh APK ký v2. Để giảm thiểu cuộc tấn công này, các APK ký v2 cũng được 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 của chúng. Giá trị của thuộc tính là một tập hợp các 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 được yêu cầu từ chối các APK không có chữ ký cho lược đồ chữ ký APK mà người xác minh thích từ tập hợp này (ví dụ: lược đồ v2). Sự 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 xác minh APK đã 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 Gói chữ ký APK v2 Block. Để giảm thiểu cuộc tấn công này, danh sách các ID thuật toán chữ ký mà APK đã được ký được lưu trữ trong khối signed data
được bảo vệ bởi mỗi chữ ký.
xác minh
Trong Android 7.0 trở lên, các APK có thể được xác minh theo Sơ đồ 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.
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
- Tìm Khối ký APK và xác minh rằng:
- Hai trường kích thước của Khối ký APK chứa cùng một giá trị.
- ZIP Central Directory ngay sau ZIP Bản ghi cuối Central Directory.
- ZIP End of Central Directory không có thêm dữ liệu theo sau.
- Xác định vị trí Khối chữ ký APK đầu tiên v2 bên trong Khối chữ ký APK. Nếu Khối v2 nếu có, 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 .
- Đối với mỗi
signer
trong Khối sơ đồ chữ ký APK v2:- Chọn
signature algorithm ID
được hỗ trợ mạnh nhất từ cácsignatures
. Thứ tự sức mạnh tùy thuộc vào từng phiên bản triển khai / nền tảng. - Xác minh
signature
tương ứng từ cácsignatures
so vớisigned data
bằngpublic key
. (Bây giờ có thể an toàn để phân tích cú phápsigned data
.) - Xác minh rằng danh sách các ID thuật toán chữ ký đã được sắp xếp trong các thông
digests
vàsignatures
là giống hệt nhau. (Điều này là để ngăn chặn việc xóa / thêm chữ ký.) - Tính toán thông báo về nội dung APK bằng cách sử dụng thuật toán thông báo giống như thuật toán thông báo được sử dụng bởi thuật toán chữ ký.
- Xác minh rằng thông báo được tính toán giống với
digest
tương ứng từ các thôngdigests
. - Xác minh rằng SubjectPublicKeyInfo của chứng chỉ đầu tiên của
certificate
certificates
giống vớipublic key
.
- Chọn
- 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 cho mỗisigner
được tìm thấy.
Lưu ý : Không được xác minh APK bằng lược đồ v1 nếu lỗi xảy ra ở bước 3 hoặc 4.
Xác minh APK do JAR ký (lược đồ v1)
APK được ký bằng JAR là JAR được ký chuẩn , phải chứa chính xác các mục nhập đượ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:
- 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).
- <signer>. (RSA | DSA | EC) là một PKCS # 7 CMS ContentInfo với cấu trúc SignedData có chữ ký được xác minh qua tệp <signer> .SF.
- Tệp <signer> .SF chứa toàn bộ tệp thông báo về META-INF / MANIFEST.MF và thông báo của từng phần của META-INF / MANIFEST.MF. Thông báo toàn bộ tệp của MANIFEST.MF đã được xác minh. Nếu không thành công, thông báo của từng phần MANIFEST.MF sẽ được xác minh thay thế.
- 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 thông báo về nội dung không nén của mục nhập. Tất cả những tiêu chuẩn này đã được xác minh.
- 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.