💡 Ví dụ hoạt động đầy đủ có sẵn trên GitHub:
digital-signing-certificate-validity-dotnet
Vấn đề Tuân thủ mà Không Ai Nhìn Thấy Cho đến Khi Kiểm Toán Thực Hiện
Một dịch vụ ký hoạt động trong ba năm mà không gặp lỗi. Các tài liệu được gửi đi, người nhận chấp nhận chúng, và không có gì trong nhật ký gợi ý vấn đề. Sau đó, bộ kiểm tra của đối tác đánh dấu một lô tài liệu là không hợp lệ, và cuộc điều tra phát hiện hai nguyên nhân: các chữ ký được tạo bằng SHA-1, và trong bốn tháng qua chứng chỉ đã hết hạn.
Cả hai lỗi đều im lặng tại thời điểm ký. Đó là điều mà GroupDocs.Signature 26.9 thay đổi.
Việc thực thi tính hợp lệ của chứng chỉ là hành vi mặc định mới cho việc ký số .NET: một chứng chỉ ngoài khoảng thời gian hợp lệ sẽ bị từ chối thay vì được sử dụng. Nó đi kèm với hai thành phần phụ - SHA-256 làm hàm băm PDF mặc định, và một LogLevel cuối cùng có khả năng lọc - và cùng nhau chúng chuyển ba loại lỗi từ người nhận trở lại người gửi, nơi chúng vẫn có thể được khắc phục.
Tại sao Thành công Im lặng lại là Kết quả Đắt đỏ
Ký số là điều bất thường vì bên gây ra lỗi không phải là bên phát hiện nó. Một hoá đơn sai định dạng sẽ thất bại trong hệ thống của bạn; một chữ ký không hợp lệ sẽ thất bại trong hệ thống của người khác, vài tuần sau, mà không có bất kỳ chẩn đoán nào bạn có thể đọc được.
Sự bất đối xứng này là lý do tại sao “API trả về thành công” không phải là một cam kết hữu ích ở đây. Các mặc định cũ được tối ưu để không làm gián đoạn người gọi, và chi phí rơi vào người nhận và cuối cùng là người phải ký lại và gửi lại hàng trăm tài liệu.
Thay đổi 1: Chứng chỉ đã Hết hạn sẽ bị Từ chối
Sự thay đổi chính. Sign hiện nay ném ra GroupDocsSignatureException khi thời gian hiệu lực của chứng chỉ đã kết thúc hoặc chưa bắt đầu, và không có gì được ghi vào đĩa.
try
{
signature.Sign(outputPath, options);
return true;
}
catch (GroupDocsSignatureException ex)
{
Console.WriteLine($" Rejected: {ex.Message}");
return false;
}
Thông báo ghi rõ tên chứng chỉ và thuộc tính cho phép nó, vì vậy một người vận hành đọc dòng nhật ký có thể hành động mà không cần mở tài liệu. Đối với một pipeline nâng cấp lên 26.9 và bắt đầu gặp lỗi, đây hầu như luôn là nguyên nhân - và phản hồi đúng là gia hạn, không phải ẩn lỗi.
Khi bạn thực sự cần hành vi cũ, chỉ cần một thuộc tính:
var options = new DigitalSignOptions(certificate)
{
Password = certificatePassword,
AllowExpired = true
};
Tài liệu được ký và một cảnh báo được gửi tới logger. Các bộ kiểm tra vẫn từ chối kết quả, vì AllowExpired điều khiển những gì thư viện cho phép chứ không phải giá trị thực của chứng chỉ. Cờ phụ AllowNotYetValid bao phủ phần còn lại của khoảng thời gian và được thiết kế độc lập: cho phép một chứng chỉ đã hết hạn không đồng nghĩa với việc cho phép một chứng chỉ có ngày hiệu lực trong tương lai.
Thay đổi 2: SHA-256 là Mặc định
Chữ ký số PDF hiện nay được viết bằng SHA-256 trong định dạng adbe.pkcs7.detached mà các bộ kiểm tra hiện tại mong đợi. Các phiên bản trước viết bằng SHA-1.
var options = new DigitalSignOptions(certificate)
{
Password = certificatePassword,
HashAlgorithm = HashAlgorithm.Sha256,
Reason = "Approved",
Location = "Head office"
};
Việc đặt thuộc tính một cách rõ ràng chỉ cần thiết khi muốn sử dụng các thuật toán mạnh hơn - Sha384 hoặc Sha512 khi chính sách yêu cầu - hoặc để giữ lại Sha1 cho bộ kiểm tra không thể xử lý các thuật toán khác. Một dấu thời gian được thêm vào chữ ký cũng sử dụng cùng hàm băm.
Việc xác minh đã thay đổi trong cùng phiên bản và cùng hướng: DigitalVerifyOptions không có tiêu chí nào trước đây gần như không làm gì, và bây giờ thực hiện một kiểm tra mật mã đầy đủ, vì vậy một tài liệu bị thay đổi sau khi ký sẽ được báo là không hợp lệ.
Thay đổi 3: LogLevel Thực sự Lọc
SignatureSettings đã chấp nhận một logger từ lâu. Trước 26.9 mức độ log bị bỏ qua, vì vậy mọi thông điệp đều được nhận bất kể và hầu hết các dịch vụ tắt logging thay vì bị ngập trong các trace.
Mẫu này làm cho sự khác biệt có thể đo lường bằng cách ký cùng một tài liệu ba lần với một logger đếm:
var levels = new Dictionary<string, LogLevel>
{
["None"] = LogLevel.None,
["Warning | Error"] = LogLevel.Warning | LogLevel.Error,
["All"] = LogLevel.All
};
None tạo ra không có thông điệp nào, Warning | Error giữ lại cảnh báo duy nhất được đưa ra bởi chứng chỉ đã hết hạn nhưng được cho phép, và All thêm một trace cho mỗi bước. Logger đếm này chính là điểm tích hợp cho ngăn xếp của bạn:
public void Warning(string message)
{
Warnings++;
WarningMessages.Add(message);
}
Triển khai ba phương thức này với Serilog, NLog hoặc Application Insights và các chẩn đoán của thư viện sẽ xuất hiện ở bất kỳ nơi nào mà các log của dịch vụ của bạn được ghi.
Mức độ log có thay đổi các ngoại lệ tôi nhận được không?
Không, và đáng để nêu rõ vì hai thứ này có vẻ liên quan. LogLevel lọc những gì đến ILogger. Các ngoại lệ vẫn được ném ra mã của bạn bất kể: một chứng chỉ đã hết hạn mà không có AllowExpired vẫn sẽ ném ngoại lệ ở LogLevel.None, và khối catch của bạn sẽ hành xử giống nhau. Chẩn đoán và luồng điều khiển là các kênh riêng biệt, đó là lý do khiến việc chạy môi trường production ở mức Warning | Error an toàn.
Việc Từ chối Rẻ hơn So với Nhìn thấy
Sự phản đối đối với việc dừng cứng là vấn đề vận hành: một batch hàng đêm trước đây hoàn thành giờ bây giờ thất bại vào lúc 02:00 và ai đó nhận được thông báo.
Đó là một chi phí thực tế, và vẫn là chi phí nhỏ hơn. Một batch bị từ chối chỉ tạo ra một cảnh báo, một lần gia hạn và một lần chạy lại, tất cả trong hệ thống của bạn. Một batch ký bằng chứng chỉ đã hết hạn được người nhận phát hiện, đồng nghĩa với một luồng hỗ trợ, việc phát hành lại mọi tài liệu bị ảnh hưởng, và một cuộc trò chuyện khó xử về thời gian đã diễn ra.
Mẫu này làm cho lỗi trở nên cụ thể thay vì lý thuyết: nó cố ý ký bằng chứng chỉ đã hết hạn, bắt ngoại lệ và in thông điệp, vì vậy bạn có thể thấy chính xác những gì log của bạn sẽ chứa trước khi nâng cấp đến môi trường production.
Tôi khuyên bạn chạy phương thức đó với kho chứng chỉ của mình trước khi lên lịch nâng cấp phiên bản.
Những việc Cần làm Trước khi Nâng cấp
Ba kiểm tra, theo thứ tự khả năng gây vấn đề.
Hãy kiểm tra ngày hết hạn chứng chỉ trên mọi đường dẫn ký, bao gồm cả những đường chạy hàng tháng hoặc hàng quý - đó là nơi chứng chỉ hết hạn ẩn lâu nhất. Sau đó tìm kiếm HashAlgorithm: nếu không có gì thiết lập nó, hàm băm của bạn sẽ chuyển từ SHA-1 sang SHA-256 khi nâng cấp, đây là một cải tiến mà vẫn nên ghi trong ghi chú phát hành. Cuối cùng, quyết định mức độ log một cách có chủ đích. Mặc định trung thực cho một dịch vụ là Warning | Error; All dùng để tái tạo một vấn đề cụ thể, và None có nghĩa là bỏ qua tín hiệu duy nhất cho biết chữ ký được tạo dưới một sự miễn trừ.
Việc Xác minh Thay đổi theo cùng Hướng
Dễ bỏ qua, vì không có gì trong mã gọi cần thay đổi. DigitalVerifyOptions không có tiêu chí nào trước đây gần như không làm gì: nó so sánh các tiêu chí được cung cấp, và nếu không có, nó không có gì để nói. Từ phiên bản 26.9, cùng một lời gọi thực hiện một kiểm tra mật mã đầy đủ cho mọi chữ ký số PDF.
Đối với một dịch vụ xác minh tài liệu đến, đây là một nâng cấp im lặng từ “có một chữ ký ở đây” sang “chữ ký này khớp với nội dung này”. Nên biết trước khi bạn thấy một tài liệu bắt đầu thất bại trong việc xác minh mà tháng trước đã vượt qua: tài liệu có thể đã bị thay đổi, và kiểm tra cũ chỉ đơn giản không kiểm tra.
Các Chứng chỉ trong Mẫu
Một chi tiết đáng sao chép hơn là mã: mẫu không kèm theo khóa riêng. TestCertificates.cs tạo ba PFX tự ký trong bộ nhớ khi chạy - hợp lệ, hết hạn năm ngoái, hợp lệ từ năm tới - vì vậy bản demo hoạt động bất kể ngày hiện tại và không có gì nhạy cảm trong kho lưu trữ.
Mẫu này đáng được áp dụng trong bộ kiểm thử của bạn. Một chứng chỉ kiểm thử đã cam kết sẽ hết hạn cuối cùng, và khi điều đó xảy ra lỗi sẽ trông giống hệt lỗi mà bản phát hành này được xây dựng để phát hiện.
Kết luận
Ba thay đổi, một hướng: các lỗi trước đây xuất hiện ở người nhận giờ xuất hiện ở người gửi. Hãy gia hạn chứng chỉ thay vì dùng AllowExpired, để SHA-256 làm mặc định, xác minh tài liệu đến bằng mật mã, và chọn mức độ log trước khi bạn cần. Mẫu chạy tất cả sáu hành vi trong một lần, bao gồm cả việc từ chối, vì vậy việc nâng cấp có thể được diễn tập trong vài phút.