本文介紹了使用DSS(CMS容器)確保LTV驗證的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
我正在開發一個解決方案,該解決方案允許在遠程服務器上使用p12證書進行簽名。
首先,我擁有在一臺服務器上計算的文檔摘要,然后將其發送到另一臺服務器上進行簽名。
這里是PDF文件,您將找到兩個PDF版本。";CURRENT_SIGNATURE.pdf";文件是我使用下面的代碼獲得的結果。而";TARGER_SIGNATUREPDF.pdf";正是我想要的目標。正如您所看到的,目標文件顯示的證書吊銷列表嵌入到簽名中。另一方面,當前表示文檔中包含的證書吊銷列表。此外,目標文件只有一個簽名,沒有添加修訂版本
:
https://www.grosfichiers.com/i4fmqCz43is
結果審核:
我現在的目標是添加LTV驗證,因為我知道我正在使用PadesCMSSignedDataBuilder在服務器部分簽名
服務器A上的*
public class ServerA {
private static PAdESSignatureParameters signatureParameters;
private static DSSDocument documentToSign;
public static ExternalCMSPAdESService service;
private static final String TSA_URL = "http://dss.nowina.lu/pki-factory/tsa/good-tsa";
public static void main(String[] args) throws Exception {
documentToSign = new FileDocument(new File("Doc 2.pdf"));
signatureParameters = new PAdESSignatureParameters();
signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
signatureParameters.setLocation("Luxembourg");
signatureParameters.setReason("DSS testing");
signatureParameters.setContactInfo("Jira");
signatureParameters.setGenerateTBSWithoutCertificate(true);
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
commonCertificateVerifier.setCrlSource(new OnlineCRLSource());
commonCertificateVerifier.setOcspSource(new OnlineOCSPSource());
commonCertificateVerifier.setCheckRevocationForUntrustedChains(true);
service = new ExternalCMSPAdESService(commonCertificateVerifier);
byte[] documentDigest = computeDocumentDigest(documentToSign, signatureParameters);
// Embedded CAdES is generated by a third party
byte[] cmsSignedData = ServerB.getSignedCMSignedData(documentDigest);
service.setCmsSignedData(cmsSignedData);
DSSDocument finalDoc = service.signDocument(documentToSign, signatureParameters, null);
PAdESService service = new PAdESService(commonCertificateVerifier);
TimestampDataLoader timestampDataLoader = new TimestampDataLoader();// uses the specific content-type
OnlineTSPSource tsa1 = new OnlineTSPSource("http://dss.nowina.lu/pki-factory/tsa/ee-good-tsa");
tsa1.setDataLoader(timestampDataLoader);
service.setTspSource(tsa1);
PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
extensionParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_LT);
DSSDocument extendedDocument = service.extendDocument(finalDoc, extensionParameters);
save(finalDoc);
save2(extendedDocument);
}
private static void save(DSSDocument signedDocument) {
try (FileOutputStream fos = new FileOutputStream("DSS.pdf")) {
Utils.copy(signedDocument.openStream(), fos);
} catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
alert.showAndWait();
return;
}
}
private static void save2(DSSDocument signedDocument) {
try (FileOutputStream fos = new FileOutputStream("DSS-2.pdf")) {
Utils.copy(signedDocument.openStream(), fos);
} catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
alert.showAndWait();
return;
}
}
public static CertificateVerifier getOfflineCertificateVerifier() {
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setDataLoader(new IgnoreDataLoader());
return cv;
}
protected static byte[] computeDocumentDigest(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters) {
IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
final PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();
return pdfSignatureService.digest(toSignDocument, parameters);
}
private static class ExternalCMSPAdESService extends PAdESService {
private static final long serialVersionUID = -2003453716888412577L;
private byte[] cmsSignedData;
public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
}
@Override
protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
final SignatureValue signatureValue) {
if (this.cmsSignedData == null) {
throw new NullPointerException("A CMS signed data must be provided");
}
return this.cmsSignedData;
}
public void setCmsSignedData(final byte[] cmsSignedData) {
this.cmsSignedData = cmsSignedData;
}
}
}
并能夠對計算出的哈希進行簽名:
服務器B上的*
public class ServerB {
private static PAdESSignatureParameters signatureParameters;
private static DSSDocument documentToSign;
public static ExternalCMSPAdESService service;
/**
* Computes a CAdES with specific things for PAdES
*/
public static byte[] getSignedCMSignedData(byte[] documentDigest) throws Exception {
signatureParameters = new PAdESSignatureParameters();
signatureParameters.setSigningCertificate(getSigningCert());
signatureParameters.setCertificateChain(getCertificateChain());
signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
signatureParameters.setLocation("Luxembourg");
signatureParameters.setReason("DSS testing");
signatureParameters.setContactInfo("Jira");
CMSProcessableByteArray content = new CMSProcessableByteArray(documentDigest);
PadesCMSSignedDataBuilder padesCMSSignedDataBuilder = new PadesCMSSignedDataBuilder(getOfflineCertificateVerifier());
SignatureAlgorithm signatureAlgorithm = signatureParameters.getSignatureAlgorithm();
CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId());
SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(signatureParameters, documentDigest);
CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner,
signerInfoGeneratorBuilder, null);
CMSUtils.generateDetachedCMSSignedData(generator, content);
SignatureTokenConnection signingToken = new Pkcs12SignatureToken("certificate.p12",
new KeyStore.PasswordProtection("123456".toCharArray()));
DSSPrivateKeyEntry privateKey = getKey("certificate.p12","123456");
SignatureValue signatureValue = signingToken.sign(new ToBeSigned(customContentSigner.getOutputStream().toByteArray()),
signatureParameters.getDigestAlgorithm(), privateKey);
customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue.getValue());
generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner, signerInfoGeneratorBuilder, null);
CMSSignedData cmsSignedData = CMSUtils.generateDetachedCMSSignedData(generator, content);
return DSSASN1Utils.getDEREncoded(cmsSignedData);
}
public static CertificateVerifier getOfflineCertificateVerifier() {
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setDataLoader(new IgnoreDataLoader());
return cv;
}
public static List<CertificateToken> getCertificateChain() throws Exception {
List<CertificateToken> list = new ArrayList<>();
CertificateToken[] l = getKey("certificate.p12","123456").getCertificateChain();
for (int i = 0; i < l.length; i++) {
list.add(l[i]);
}
return list;
}
public static CertificateToken getSigningCert() throws Exception {
return getKey("certificate.p12","123456").getCertificate();
}
public static DSSPrivateKeyEntry getKey(String certificate, String pin) throws Exception {
try (Pkcs12SignatureToken signatureToken = new Pkcs12SignatureToken("certificate.p12",
new KeyStore.PasswordProtection("123456".toCharArray()))) {
List<DSSPrivateKeyEntry> keys = signatureToken.getKeys();
KSPrivateKeyEntry dssPrivateKeyEntry = (KSPrivateKeyEntry) keys.get(0);
DSSPrivateKeyEntry entry = signatureToken.getKey(dssPrivateKeyEntry.getAlias(),
new KeyStore.PasswordProtection("123456".toCharArray()));
return entry;
}
}
private static class ExternalCMSPAdESService extends PAdESService {
private static final long serialVersionUID = -2003453716888412577L;
private byte[] cmsSignedData;
public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
}
@Override
protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
final SignatureValue signatureValue) {
if (this.cmsSignedData == null) {
throw new NullPointerException("A CMS signed data must be provided");
}
return this.cmsSignedData;
}
public void setCmsSignedData(final byte[] cmsSignedData) {
this.cmsSignedData = cmsSignedData;
}
}
}
推薦答案
在an answer to another question的備注中,開始了一場討論,您在該討論中指出了此問題并請求幫助。在那次討論中,很明顯,你還不完全知道你想要達到什么目的。因此,讓我們稍微澄清一下。
LTV驗證
您說要將LTV驗證添加到您的簽名。讓我們先來看看這意味著什么。
LTV是LOngTV的縮寫。它所代表的目標是確保簽名在若干年后仍可被驗證。
這個目標試圖克服的挑戰是,從長遠來看,驗證者需要的信息將無法在網上獲得,并且使用的算法最終將不再被認為是安全的。
方法是檢索所需信息一次,并以可信的方式將其與簽名捆綁在一起,并應用數字時間戳來證明特定的數據、簽名和額外信息集存在并在給定時間(例如,當使用的簽名算法仍被認為強大時)。
到目前為止,一切順利。
Adobe早期(在PDF成為ISO標準之前)定義了一種實現LTV的機制:他們指定了一個特定的簽名屬性,用戶應該在簽名之前將驗證所需的數據收集到該屬性中,并且他們建議對嵌入的簽名容器應用時間戳。
然而,從那時起,這種機制被證明過于簡單和靜態。根據使用的驗證模型,在簽名之前收集的信息不夠好:要檢查給定的證書在簽名時是否有效,嚴格地說,需要在簽名時間之后生成的信息。若要處理算法變弱的問題,可能需要反復為整個文檔加時間戳。
為了處理這個ETSI(一個歐洲標準化組織),指定了向文檔添加與驗證相關的信息的替代方法,以及添加覆蓋整個文檔(而不僅僅是嵌入的簽名容器)的額外時間戳的方法。這些機制不會更改原始簽名容器,但會將增量更新中的信息添加到原始文檔中。同時,這些機制已被添加到ISO 32000-2的國際PDF標準中。它們被歸入PADES一詞。
ETSI還定義了如何使用這些新機制以可互操作的方式增強簽名的標準方案,PADES基線配置文件:
B級別僅包含配置為特別包括ESS證書ID屬性的基本簽名容器。
T級別基于B級別,但另外還需要簽名后時間戳。此時間戳可以作為簽名時間戳應用于原始簽名容器,也可以作為文檔的額外增量更新中的文檔時間戳。
LT級別基于T級別,需要在新的增量更新中添加缺少的中間證書和必需的吊銷信息。
LTA級別基于LT級別,需要在另一次增量更新中添加另一個文檔時間戳。
為了實現長期驗證,可以重復添加LT和LTA,以提供上一個時間戳的驗證信息,并記錄所使用的算法是在它們仍然強大的時候應用的。
Adobe已經建立了他們自己的啟用LTV的配置文件,該配置文件假定對驗證數據的要求不那么嚴格(不需要時間戳),并且不關心算法會變得薄弱。他們基本上收集了他們在文檔中找到的所有與驗證相關的信息,并按原樣使用它們。(更確切地說,這是Adobe Acrobat的標準設置的行為。您可以對Acrobat進行不同的配置以更改要求,例如,使某些時間戳做重要。因此,在談論啟用LTV的簽名時,請始終確保您與您的討論伙伴具有相同的設置…)
使用eSig DSS進行擴展
如果要在服務器A上使用eSig DSS擴展PDF簽名,只需使用finalDoc和
PAdESService service = new PAdESService(certificateVerifier);
service.setTspSource(tspSource);
PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
extensionParameters.setSignatureLevel(extensionLevel);
DSSDocument extendedDocument = service.extendDocument(finalDoc, extensionParameters);
在哪里
certificateVerifier是為在線資源初始化的CommonCertificateVerifier
tspSource是為您選擇的時間戳服務初始化的OnlineTSPSource,
extensionLevel為所需級別,如SignatureLevel.PAdES_BASELINE_LT。
extendedDocument中的結果應包含所需的驗證相關信息。
這篇關于使用DSS(CMS容器)確保LTV驗證的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,






