Skip to content

Commit 19ee24c

Browse files
committed
Determine the default digest algorithm based on the signature algorithm in PdfPadesSigner
DEVSIX-7961
1 parent 15993f5 commit 19ee24c

File tree

10 files changed

+133
-20
lines changed

10 files changed

+133
-20
lines changed

bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/openssl/PEMParserBC.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.bouncycastle.openssl;
2424

25+
import com.itextpdf.bouncycastle.asn1.pcks.PrivateKeyInfoBC;
2526
import com.itextpdf.bouncycastle.cert.X509CertificateHolderBC;
2627
import com.itextpdf.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBC;
2728
import com.itextpdf.commons.bouncycastle.openssl.IPEMParser;
2829

2930
import java.io.IOException;
3031
import java.util.Objects;
32+
33+
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
3134
import org.bouncycastle.cert.X509CertificateHolder;
3235
import org.bouncycastle.openssl.PEMParser;
3336
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
@@ -68,6 +71,9 @@ public Object readObject() throws IOException {
6871
if (readObject instanceof PKCS8EncryptedPrivateKeyInfo) {
6972
return new PKCS8EncryptedPrivateKeyInfoBC((PKCS8EncryptedPrivateKeyInfo) readObject);
7073
}
74+
if (readObject instanceof PrivateKeyInfo) {
75+
return new PrivateKeyInfoBC((PrivateKeyInfo) readObject);
76+
}
7177
return readObject;
7278
}
7379

bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/openssl/PEMParserBCFips.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.bouncycastlefips.openssl;
2424

25+
import com.itextpdf.bouncycastlefips.asn1.pcks.PrivateKeyInfoBCFips;
2526
import com.itextpdf.bouncycastlefips.cert.X509CertificateHolderBCFips;
2627
import com.itextpdf.bouncycastlefips.pkcs.PKCS8EncryptedPrivateKeyInfoBCFips;
2728
import com.itextpdf.commons.bouncycastle.openssl.IPEMParser;
2829

2930
import java.io.IOException;
3031
import java.util.Objects;
32+
33+
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
3134
import org.bouncycastle.cert.X509CertificateHolder;
3235
import org.bouncycastle.openssl.PEMParser;
3336
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
@@ -68,6 +71,9 @@ public Object readObject() throws IOException {
6871
if (readObject instanceof PKCS8EncryptedPrivateKeyInfo) {
6972
return new PKCS8EncryptedPrivateKeyInfoBCFips((PKCS8EncryptedPrivateKeyInfo) readObject);
7073
}
74+
if (readObject instanceof PrivateKeyInfo) {
75+
return new PrivateKeyInfoBCFips((PrivateKeyInfo) readObject);
76+
}
7177
return readObject;
7278
}
7379

sign/src/main/java/com/itextpdf/signatures/PdfPadesSigner.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public void signWithBaselineBProfile(SignerProperties signerProperties, Certific
117117
public void signWithBaselineBProfile(SignerProperties signerProperties, Certificate[] chain, PrivateKey privateKey)
118118
throws GeneralSecurityException, IOException {
119119
IExternalSignature externalSignature =
120-
new PrivateKeySignature(privateKey, DEFAULT_DIGEST_ALGORITHM, FACTORY.getProviderName());
120+
new PrivateKeySignature(privateKey, getDigestAlgorithm(privateKey), FACTORY.getProviderName());
121121
signWithBaselineBProfile(signerProperties, chain, externalSignature);
122122
}
123123

@@ -151,7 +151,7 @@ public void signWithBaselineTProfile(SignerProperties signerProperties, Certific
151151
public void signWithBaselineTProfile(SignerProperties signerProperties, Certificate[] chain, PrivateKey privateKey,
152152
ITSAClient tsaClient) throws GeneralSecurityException, IOException {
153153
IExternalSignature externalSignature =
154-
new PrivateKeySignature(privateKey, DEFAULT_DIGEST_ALGORITHM, FACTORY.getProviderName());
154+
new PrivateKeySignature(privateKey, getDigestAlgorithm(privateKey), FACTORY.getProviderName());
155155
signWithBaselineTProfile(signerProperties, chain, externalSignature, tsaClient);
156156
}
157157

@@ -196,7 +196,7 @@ public void signWithBaselineLTProfile(SignerProperties signerProperties, Certifi
196196
public void signWithBaselineLTProfile(SignerProperties signerProperties, Certificate[] chain, PrivateKey privateKey,
197197
ITSAClient tsaClient) throws GeneralSecurityException, IOException {
198198
IExternalSignature externalSignature =
199-
new PrivateKeySignature(privateKey, DEFAULT_DIGEST_ALGORITHM, FACTORY.getProviderName());
199+
new PrivateKeySignature(privateKey, getDigestAlgorithm(privateKey), FACTORY.getProviderName());
200200
signWithBaselineLTProfile(signerProperties, chain, externalSignature, tsaClient);
201201
}
202202

@@ -242,7 +242,7 @@ public void signWithBaselineLTAProfile(SignerProperties signerProperties, Certif
242242
public void signWithBaselineLTAProfile(SignerProperties signerProperties, Certificate[] chain,
243243
PrivateKey privateKey, ITSAClient tsaClient) throws GeneralSecurityException, IOException {
244244
IExternalSignature externalSignature =
245-
new PrivateKeySignature(privateKey, DEFAULT_DIGEST_ALGORITHM, FACTORY.getProviderName());
245+
new PrivateKeySignature(privateKey, getDigestAlgorithm(privateKey), FACTORY.getProviderName());
246246
signWithBaselineLTAProfile(signerProperties, chain, externalSignature, tsaClient);
247247
}
248248

@@ -515,4 +515,16 @@ private void createRevocationClients(Certificate signingCert, boolean clientsReq
515515
ocspClient = new OcspClientBouncyCastle(null);
516516
}
517517
}
518+
519+
private String getDigestAlgorithm(PrivateKey privateKey) {
520+
String signatureAlgorithm = SignUtils.getPrivateKeyAlgorithm(privateKey);
521+
switch (signatureAlgorithm) {
522+
case "Ed25519":
523+
return DigestAlgorithms.SHA512;
524+
case "Ed448":
525+
return DigestAlgorithms.SHAKE256;
526+
default:
527+
return DEFAULT_DIGEST_ALGORITHM;
528+
}
529+
}
518530
}

sign/src/main/java/com/itextpdf/signatures/PrivateKeySignature.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ This file is part of the iText (R) project.
2727
import com.itextpdf.signatures.exceptions.SignExceptionMessageConstant;
2828

2929
import java.security.GeneralSecurityException;
30-
import java.security.NoSuchAlgorithmException;
3130
import java.security.PrivateKey;
3231
import java.security.Signature;
3332

sign/src/test/java/com/itextpdf/signatures/sign/PdfPadesSignerTest.java

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ This file is part of the iText (R) project.
3232
import com.itextpdf.kernel.exceptions.PdfException;
3333
import com.itextpdf.kernel.geom.Rectangle;
3434
import com.itextpdf.kernel.pdf.PdfReader;
35-
import com.itextpdf.kernel.pdf.StampingProperties;
3635
import com.itextpdf.signatures.*;
3736
import com.itextpdf.signatures.exceptions.SignExceptionMessageConstant;
3837
import com.itextpdf.signatures.testutils.PemFileHelper;
@@ -41,22 +40,24 @@ This file is part of the iText (R) project.
4140
import com.itextpdf.signatures.testutils.client.TestOcspClient;
4241
import com.itextpdf.signatures.testutils.client.TestTsaClient;
4342
import com.itextpdf.test.ExtendedITextTest;
44-
import com.itextpdf.test.annotations.type.IntegrationTest;
43+
import com.itextpdf.test.annotations.type.BouncyCastleIntegrationTest;
44+
import org.junit.Assert;
45+
import org.junit.Assume;
46+
import org.junit.BeforeClass;
47+
import org.junit.Test;
48+
import org.junit.experimental.categories.Category;
4549

4650
import java.io.IOException;
4751
import java.security.GeneralSecurityException;
52+
import java.security.NoSuchAlgorithmException;
4853
import java.security.PrivateKey;
4954
import java.security.Security;
5055
import java.security.cert.Certificate;
5156
import java.security.cert.CertificateException;
5257
import java.security.cert.X509Certificate;
5358
import java.util.Arrays;
54-
import org.junit.Assert;
55-
import org.junit.BeforeClass;
56-
import org.junit.Test;
57-
import org.junit.experimental.categories.Category;
5859

59-
@Category(IntegrationTest.class)
60+
@Category(BouncyCastleIntegrationTest.class)
6061
public class PdfPadesSignerTest extends ExtendedITextTest {
6162
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
6263

@@ -65,6 +66,8 @@ public class PdfPadesSignerTest extends ExtendedITextTest {
6566
private static final String destinationFolder = "./target/test/com/itextpdf/signatures/sign/PdfPadesSignerTest/";
6667
private static final char[] password = "testpassphrase".toCharArray();
6768

69+
private static final boolean FIPS_MODE = "BCFIPS".equals(FACTORY.getProviderName());
70+
6871
@BeforeClass
6972
public static void before() {
7073
Security.addProvider(FACTORY.getProvider());
@@ -106,7 +109,7 @@ public void directoryPathIsNotADirectoryTest()
106109
Assert.assertEquals(MessageFormatUtil.format(SignExceptionMessageConstant.PATH_IS_NOT_DIRECTORY,
107110
destinationFolder + "newPdf.pdf"), exception.getMessage());
108111
}
109-
112+
110113
@Test
111114
public void noSignaturesToProlongTest()
112115
throws CertificateException, IOException, AbstractOperatorCreationException, AbstractPKCSException {
@@ -132,7 +135,7 @@ public void noSignaturesToProlongTest()
132135
() -> padesSigner.prolongSignatures(testTsa));
133136
Assert.assertEquals(SignExceptionMessageConstant.NO_SIGNATURES_TO_PROLONG, exception.getMessage());
134137
}
135-
138+
136139
@Test
137140
public void defaultClientsCannotBeCreated()
138141
throws IOException, AbstractOperatorCreationException, AbstractPKCSException, CertificateException {
@@ -189,7 +192,7 @@ public void defaultSignerPropertiesTest()
189192
TestTsaClient testTsa = new TestTsaClient(Arrays.asList(tsaChain), tsaPrivateKey);
190193
ICrlClient crlClient = new TestCrlClient().addBuilderForCertIssuer(caCert, caPrivateKey);
191194
TestOcspClient ocspClient = new TestOcspClient().addBuilderForCertIssuer(caCert, caPrivateKey);
192-
195+
193196
padesSigner.setOcspClient(ocspClient).setCrlClient(crlClient);
194197

195198
padesSigner.signWithBaselineLTAProfile(signerProperties, signRsaChain, pks, testTsa);
@@ -237,7 +240,53 @@ public int getTokenSizeEstimate() {
237240
Assert.assertEquals(MessageFormatUtil.format(
238241
SignExceptionMessageConstant.TOKEN_ESTIMATION_SIZE_IS_NOT_LARGE_ENOUGH, 1024, 2780), e.getMessage());
239242
}
240-
243+
244+
@Test
245+
public void padesSignatureEd25519Test()
246+
throws IOException, GeneralSecurityException, AbstractOperatorCreationException, AbstractPKCSException {
247+
Assume.assumeFalse(FACTORY.isInApprovedOnlyMode());
248+
String fileName = "padesSignatureEd25519Test.pdf";
249+
String outFileName = destinationFolder + fileName;
250+
String cmpFileName = sourceFolder + "cmp_" + fileName;
251+
String srcFileName = sourceFolder + "helloWorldDoc.pdf";
252+
String signCertFileName = certsSrc + "signCertEd25519.pem";
253+
254+
Certificate[] signEdDSAChain = PemFileHelper.readFirstChain(signCertFileName);
255+
PrivateKey signEdDSAPrivateKey = PemFileHelper.readFirstKey(signCertFileName, password);
256+
257+
SignerProperties signerProperties = createSignerProperties();
258+
PdfPadesSigner padesSigner = createPdfPadesSigner(srcFileName, outFileName);
259+
padesSigner.signWithBaselineBProfile(signerProperties, signEdDSAChain, signEdDSAPrivateKey);
260+
TestSignUtils.basicCheckSignedDoc(outFileName, "Signature1");
261+
Assert.assertNull(SignaturesCompareTool.compareSignatures(outFileName, cmpFileName));
262+
}
263+
264+
@Test
265+
public void padesSignatureEd448Test()
266+
throws IOException, GeneralSecurityException, AbstractOperatorCreationException, AbstractPKCSException {
267+
Assume.assumeFalse(FACTORY.isInApprovedOnlyMode());
268+
String fileName = "padesSignatureEd448Test.pdf";
269+
String outFileName = destinationFolder + fileName;
270+
String cmpFileName = sourceFolder + "cmp_" + fileName;
271+
String srcFileName = sourceFolder + "helloWorldDoc.pdf";
272+
String signCertFileName = certsSrc + "signCertEd448.pem";
273+
274+
Certificate[] signEdDSAChain = PemFileHelper.readFirstChain(signCertFileName);
275+
PrivateKey signEdDSAPrivateKey = PemFileHelper.readFirstKey(signCertFileName, password);
276+
277+
SignerProperties signerProperties = createSignerProperties();
278+
PdfPadesSigner padesSigner = createPdfPadesSigner(srcFileName, outFileName);
279+
if (FIPS_MODE) {
280+
// SHAKE256 is currently not supported in BCFIPS
281+
Exception exception = Assert.assertThrows(NoSuchAlgorithmException.class,
282+
() -> padesSigner.signWithBaselineBProfile(signerProperties, signEdDSAChain, signEdDSAPrivateKey));
283+
} else {
284+
padesSigner.signWithBaselineBProfile(signerProperties, signEdDSAChain, signEdDSAPrivateKey);
285+
TestSignUtils.basicCheckSignedDoc(outFileName, "Signature1");
286+
Assert.assertNull(SignaturesCompareTool.compareSignatures(outFileName, cmpFileName));
287+
}
288+
}
289+
241290
private SignerProperties createSignerProperties() {
242291
SignerProperties signerProperties = new SignerProperties();
243292
signerProperties.setFieldName("Signature1");

sign/src/test/java/com/itextpdf/signatures/testutils/PemFileHelper.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
2626
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
27+
import com.itextpdf.commons.bouncycastle.asn1.pkcs.IPrivateKeyInfo;
2728
import com.itextpdf.commons.bouncycastle.cert.IX509CertificateHolder;
2829
import com.itextpdf.commons.bouncycastle.cert.jcajce.IJcaX509CertificateConverter;
2930
import com.itextpdf.commons.bouncycastle.openssl.IPEMParser;
@@ -67,12 +68,17 @@ public static Certificate[] readFirstChain(String pemFileName) throws IOExceptio
6768

6869
public static PrivateKey readFirstKey(String pemFileName, char[] keyPass)
6970
throws IOException, AbstractOperatorCreationException, AbstractPKCSException {
70-
IPKCS8EncryptedPrivateKeyInfo key = readPrivateKey(pemFileName);
71-
if (key != null) {
71+
IPKCS8EncryptedPrivateKeyInfo pkcs8Key = readPkcs8PrivateKey(pemFileName);
72+
if (pkcs8Key != null) {
7273
IInputDecryptorProvider decProv = FACTORY.createJceOpenSSLPKCS8DecryptorProviderBuilder()
7374
.setProvider(FACTORY.getProvider()).build(keyPass);
7475
IJcaPEMKeyConverter keyConverter = FACTORY.createJcaPEMKeyConverter().setProvider(FACTORY.getProvider());
75-
return keyConverter.getPrivateKey(key.decryptPrivateKeyInfo(decProv));
76+
return keyConverter.getPrivateKey(pkcs8Key.decryptPrivateKeyInfo(decProv));
77+
}
78+
IPrivateKeyInfo key = readPrivateKey(pemFileName);
79+
if (key != null) {
80+
IJcaPEMKeyConverter keyConverter = FACTORY.createJcaPEMKeyConverter().setProvider(FACTORY.getProvider());
81+
return keyConverter.getPrivateKey(key);
7682
}
7783
return null;
7884
}
@@ -105,7 +111,7 @@ private static List<IX509CertificateHolder> readCertificates(String pemFileName)
105111
}
106112
}
107113

108-
private static IPKCS8EncryptedPrivateKeyInfo readPrivateKey(String pemFileName) throws IOException {
114+
private static IPKCS8EncryptedPrivateKeyInfo readPkcs8PrivateKey(String pemFileName) throws IOException {
109115
try (IPEMParser parser = FACTORY.createPEMParser(new FileReader(pemFileName))) {
110116
Object readObject = parser.readObject();
111117
while (!(readObject instanceof IPKCS8EncryptedPrivateKeyInfo) && readObject != null) {
@@ -114,4 +120,14 @@ private static IPKCS8EncryptedPrivateKeyInfo readPrivateKey(String pemFileName)
114120
return (IPKCS8EncryptedPrivateKeyInfo) readObject;
115121
}
116122
}
123+
124+
private static IPrivateKeyInfo readPrivateKey(String pemFileName) throws IOException {
125+
try (IPEMParser parser = FACTORY.createPEMParser(new FileReader(pemFileName))) {
126+
Object readObject = parser.readObject();
127+
while (!(readObject instanceof IPrivateKeyInfo) && readObject != null) {
128+
readObject = parser.readObject();
129+
}
130+
return (IPrivateKeyInfo) readObject;
131+
}
132+
}
117133
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MC4CAQAwBQYDK2VwBCIEIPIdKJtjErGrsgpBaM04RZa49tMWLFrKDt6fVoGDxdtZ
3+
-----END PRIVATE KEY-----
4+
-----BEGIN CERTIFICATE-----
5+
MIIBCDCBuwIUGX/b5h/jmeMPJ11mVZgEfizZc1wwBQYDK2VwMCcxCzAJBgNVBAYT
6+
AkJFMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wHhcNMjMxMjA2MTQyNzQwWhcN
7+
NDMwMjA0MTQyNzQwWjAnMQswCQYDVQQGEwJCRTEYMBYGA1UEAwwPd3d3LmV4YW1w
8+
bGUuY29tMCowBQYDK2VwAyEAZUXx5GDu4xyo6UKEOqPaxTyna6LGoUswH2ShmyO/
9+
uJswBQYDK2VwA0EAV1xEcFJunwRzH9ufTGAJ362AbadF5N+hIYd5wxKES8EOkY/2
10+
TsMbipY6uQLQJP2alusxc5+2varXxskpNGAuDQ==
11+
-----END CERTIFICATE-----
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MEcCAQAwBQYDK2VxBDsEOfrKlMzLC2KFud/xAxCCT9N8y2mPl8pKyUM3nT3uWYBK
3+
MwG7PWEKk2jVgxLBIjdRATaMFG7SrZJHQQ==
4+
-----END PRIVATE KEY-----
5+
-----BEGIN CERTIFICATE-----
6+
MIIBUzCB1AIUZ3HGGxCugYIeKkQjLrCVG90vhqkwBQYDK2VxMCcxCzAJBgNVBAYT
7+
AkJFMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wHhcNMjMxMjA2MTQ0NzE4WhcN
8+
NDMwMjA0MTQ0NzE4WjAnMQswCQYDVQQGEwJCRTEYMBYGA1UEAwwPd3d3LmV4YW1w
9+
bGUuY29tMEMwBQYDK2VxAzoAO12xP2xDv+3i6DrOUiLkKk4AYf4limP3IstJejo/
10+
IcL31H7oRZvtNrDlkpdjTmfgmX06KicNVBcAMAUGAytlcQNzALdSj5VltGHj/2OY
11+
lXLeHnwe//gUpEQQBMX+u5MHxBWApGvz1R2yrJ/NASvcQW0703M9KDGD7VY+gAYn
12+
mnPXaXL1RO8mcVpz29/hak6sFEVTFHvoo6NjlAXEjYtkbraf3jCIk77t9KD5oRi1
13+
T25XxGUUAA==
14+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)