Skip to content

Commit 9a8fe3d

Browse files
Added Tests and documentation for PEM Signing
1 parent fa53be6 commit 9a8fe3d

File tree

9 files changed

+165
-77
lines changed

9 files changed

+165
-77
lines changed

README.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,20 @@ OpenSSL can be downloaded from the [website](https://www.openssl.org/).
6464
openssl req -newkey rsa-pss -new -nodes -x509 -days 3650 -pkeyopt rsa_keygen_bits:4096 -sigopt rsa_pss_saltlen:32 -keyout key.pem -out cert.pem
6565
```
6666

67-
Next you have to convert that file to a **PKCS12** file.
67+
You also have to set the public key in the [BZST online.portal](https://online.portal.bzst.de/).
68+
Exporting the public key with OpenSSL is easy:
69+
70+
```
71+
openssl rsa -in key.pem -pubout > publicKey.pub
72+
```
73+
74+
Now you can already use these two files to sign your requests. See
75+
the [example with PEM signing](./bzst-dip-java-client-demo\src\main\java\software\xdev\ApplicationWithPem.java).
76+
77+
### Create Java KeyStore (JKS)
78+
79+
If you want to go one step further you can use the Java KeyStore. Then you have to convert the `cert.pem` file to a *
80+
*PKCS12** file.
6881

6982
```
7083
openssl pkcs12 -export -in cert.pem -inkey key.pem -out certificate.p12 -name "certificate"
@@ -85,13 +98,6 @@ certificate.keystore.password=SECRET_PASSWORD
8598
certificate.keystore.file=cert.jks
8699
```
87100

88-
You also have to set the public key in the [BZST online.portal](https://online.portal.bzst.de/).
89-
Exporting the public key with OpenSSL is easy:
90-
91-
```
92-
openssl rsa -in key.pem -pubout > publicKey.pub
93-
```
94-
95101
### Client ID
96102

97103
It's also important to use the client id provided by [BZST online.portal](https://online.portal.bzst.de/)
@@ -117,9 +123,7 @@ public static BzstDipConfiguration createConfiguration()
117123
.setClientId("abcd1234-ab12-ab12-ab12-abcdef123456")
118124
.setTaxID("123")
119125
.setTaxNumber("123")
120-
.setCertificateKeystoreInputStream(() -> ClassLoader.getSystemClassLoader()
121-
.getResourceAsStream("DemoKeystore.jks"))
122-
.setCertificateKeystorePassword("test123")
126+
.setSigningProvider(new SigningProviderByJks("DemoKeystore.jks", "test123"))
123127
.setRealmEnvironmentBaseUrl(BzstDipConfiguration.ENDPOINT_URL_TEST)
124128
.setMessageTypeIndic(BzstDipDpiMessageType.DPI_401)
125129
.setReportingPeriod(LocalDate.now())

bzst-dip-java-client/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<sonar.exclusions>
5858
src/generated/**
5959
</sonar.exclusions>
60+
<junit-jupiter.version>5.10.2</junit-jupiter.version>
6061
</properties>
6162

6263
<repositories>
@@ -144,7 +145,13 @@
144145
<dependency>
145146
<groupId>org.junit.jupiter</groupId>
146147
<artifactId>junit-jupiter-api</artifactId>
147-
<version>5.10.2</version>
148+
<version>${junit-jupiter.version}</version>
149+
<scope>test</scope>
150+
</dependency>
151+
<dependency>
152+
<groupId>org.junit.jupiter</groupId>
153+
<artifactId>junit-jupiter-params</artifactId>
154+
<version>${junit-jupiter.version}</version>
148155
<scope>test</scope>
149156
</dependency>
150157
<dependency>

bzst-dip-java-client/src/main/java/software/xdev/bzst/dip/client/exception/EncryptionException.java

Lines changed: 0 additions & 36 deletions
This file was deleted.

bzst-dip-java-client/src/main/java/software/xdev/bzst/dip/client/signing/SigningProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.security.cert.X509Certificate;
2020

2121

22+
/**
23+
* Provides a certificate and a private key to use in the {@link XmlSigner}.
24+
*/
2225
public interface SigningProvider
2326
{
2427
X509Certificate getCertificate();

bzst-dip-java-client/src/main/java/software/xdev/bzst/dip/client/signing/SigningProviderByJks.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
import software.xdev.bzst.dip.client.exception.SigningException;
2929

3030

31+
/**
32+
* Provides a certificate and a private key to use in the {@link XmlSigner} by reading the JKS-Keystore and a password.
33+
*/
3134
public class SigningProviderByJks implements SigningProvider
3235
{
3336
private static final Logger LOGGER = LoggerFactory.getLogger(SigningProviderByJks.class);

bzst-dip-java-client/src/main/java/software/xdev/bzst/dip/client/signing/SigningProviderByPem.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
20-
import java.nio.charset.Charset;
20+
import java.nio.charset.StandardCharsets;
2121
import java.security.KeyFactory;
2222
import java.security.PrivateKey;
2323
import java.security.cert.CertificateException;
@@ -31,6 +31,12 @@
3131
import software.xdev.bzst.dip.client.exception.SigningException;
3232

3333

34+
/**
35+
* Provides a certificate and a private key to use in the {@link XmlSigner} by reading two PEM files.
36+
* <p>
37+
* Default algorithm while reading the private key is {@link DEFAULT_PRIVATE_KEY_ALGORITHM}.
38+
* </p>
39+
*/
3440
public class SigningProviderByPem implements SigningProvider
3541
{
3642
public static final String DEFAULT_PRIVATE_KEY_ALGORITHM = "RSASSA-PSS";
@@ -40,6 +46,9 @@ public class SigningProviderByPem implements SigningProvider
4046
private X509Certificate certificate;
4147
private PrivateKey privateKey;
4248

49+
/**
50+
* Uses the default algorithm while reading the private key: {@link DEFAULT_PRIVATE_KEY_ALGORITHM}.
51+
*/
4352
public SigningProviderByPem(
4453
final String certificatePemFilePath,
4554
final String privateKeyPemFilePath
@@ -65,6 +74,9 @@ public SigningProviderByPem(
6574
);
6675
}
6776

77+
/**
78+
* Uses the default algorithm while reading the private key: {@link DEFAULT_PRIVATE_KEY_ALGORITHM}.
79+
*/
6880
public SigningProviderByPem(
6981
final Supplier<InputStream> certificatePemInputStream,
7082
final Supplier<InputStream> privateKeyPemInputStream
@@ -139,7 +151,7 @@ private X509Certificate readCertificate(final InputStream certificateInputStream
139151

140152
private PrivateKey readPrivateKey(final InputStream privateKeyInputStream) throws Exception
141153
{
142-
final String key = new String(privateKeyInputStream.readAllBytes(), Charset.defaultCharset());
154+
final String key = new String(privateKeyInputStream.readAllBytes(), StandardCharsets.UTF_8);
143155

144156
final String privateKeyPEM = key
145157
.replace("-----BEGIN PRIVATE KEY-----", "")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package software.xdev.bzst.dip.client;
17+
18+
import java.time.LocalDate;
19+
20+
import software.xdev.bzst.dip.client.model.configuration.BzstDipConfiguration;
21+
import software.xdev.bzst.dip.client.model.configuration.BzstDipConfigurationBuilder;
22+
import software.xdev.bzst.dip.client.model.configuration.BzstDipDpiMessageType;
23+
import software.xdev.bzst.dip.client.model.configuration.BzstDipOecdDocType;
24+
import software.xdev.bzst.dip.client.model.message.BzstDipAddressFix;
25+
import software.xdev.bzst.dip.client.signing.SigningProviderByJks;
26+
import software.xdev.bzst.dip.client.signing.SigningProviderByPem;
27+
28+
29+
public class ConfigurationTestUtil
30+
{
31+
private ConfigurationTestUtil()
32+
{
33+
}
34+
35+
public static BzstDipConfiguration getConfigurationWithJksSigning()
36+
{
37+
return createBuilderWithoutSigning()
38+
.setSigningProvider(new SigningProviderByJks("DemoKeystore.jks", "test123"))
39+
.buildAndValidate();
40+
}
41+
42+
public static BzstDipConfiguration getConfigurationWithPemSigning()
43+
{
44+
return createBuilderWithoutSigning()
45+
.setSigningProvider(new SigningProviderByPem("DemoCert.pem", "DemoKey.pem"))
46+
.buildAndValidate();
47+
}
48+
49+
private static BzstDipConfigurationBuilder createBuilderWithoutSigning()
50+
{
51+
return new BzstDipConfigurationBuilder()
52+
.setClientId("TestClient")
53+
.setTaxID("123")
54+
.setTaxNumber("123")
55+
.setSigningProvider(new SigningProviderByPem("DemoKey.pem", "DemoCert.pem"))
56+
.setRealmEnvironmentBaseUrl(BzstDipConfiguration.ENDPOINT_URL_TEST)
57+
.setMessageTypeIndic(BzstDipDpiMessageType.DPI_401)
58+
.setReportingPeriod(LocalDate.now())
59+
.setDocTypeIndic(BzstDipOecdDocType.OECD_1)
60+
.setPlatformOperatorOrganizationName("TestOrg")
61+
.setPlatformOperatorPlatformName("TestApp")
62+
.setPlatformOperatorAddress(
63+
new BzstDipAddressFix("TestCity")
64+
);
65+
}
66+
}

bzst-dip-java-client/src/test/java/software/xdev/bzst/dip/client/ReportableSellerCsvFileParserTest.java

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,31 @@
1616
package software.xdev.bzst.dip.client;
1717

1818
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static software.xdev.bzst.dip.client.ConfigurationTestUtil.getConfigurationWithJksSigning;
1920

2021
import java.io.IOException;
2122
import java.nio.file.Files;
2223
import java.nio.file.Path;
23-
import java.time.LocalDate;
2424
import java.util.List;
2525

2626
import org.junit.jupiter.api.Test;
2727

28-
import software.xdev.bzst.dip.client.model.configuration.BzstDipConfiguration;
29-
import software.xdev.bzst.dip.client.model.configuration.BzstDipConfigurationBuilder;
30-
import software.xdev.bzst.dip.client.model.configuration.BzstDipDpiMessageType;
31-
import software.xdev.bzst.dip.client.model.configuration.BzstDipOecdDocType;
32-
import software.xdev.bzst.dip.client.model.message.BzstDipAddressFix;
3328
import software.xdev.bzst.dip.client.parser.ReportableSellerCsvFileParser;
34-
import software.xdev.bzst.dip.client.signing.SigningProviderByJks;
3529
import software.xdev.bzst.dip.client.xmldocument.model.CorrectableReportableSellerType;
3630

3731

3832
class ReportableSellerCsvFileParserTest
3933
{
40-
private final BzstDipConfiguration configuration = new BzstDipConfigurationBuilder()
41-
.setClientId("TestClient")
42-
.setTaxID("123")
43-
.setTaxNumber("123")
44-
.setSigningProvider(new SigningProviderByJks("DemoKeystore.jks", "test123"))
45-
.setRealmEnvironmentBaseUrl(BzstDipConfiguration.ENDPOINT_URL_TEST)
46-
.setMessageTypeIndic(BzstDipDpiMessageType.DPI_401)
47-
.setReportingPeriod(LocalDate.now())
48-
.setDocTypeIndic(BzstDipOecdDocType.OECD_1)
49-
.setPlatformOperatorOrganizationName("TestOrg")
50-
.setPlatformOperatorPlatformName("TestApp")
51-
.setPlatformOperatorAddress(
52-
new BzstDipAddressFix("TestCity")
53-
)
54-
.buildAndValidate();
5534

5635
@Test
5736
void shouldParseSuccessfullyTest() throws IOException
5837
{
5938
final String resourceName = "src/test/resources/TestCsvData.csv";
6039

6140
final List<CorrectableReportableSellerType> reportableSeller =
62-
ReportableSellerCsvFileParser.parseCsvData(Files.readString(Path.of(resourceName)), this.configuration);
41+
ReportableSellerCsvFileParser.parseCsvData(
42+
Files.readString(Path.of(resourceName)),
43+
getConfigurationWithJksSigning());
6344

6445
// Check size
6546
assertEquals(2, reportableSeller.size());
Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,62 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package software.xdev.bzst.dip.client.signing;
217

3-
import org.junit.jupiter.api.Test;
18+
import java.util.List;
19+
import java.util.stream.Stream;
20+
21+
import org.junit.jupiter.api.Assertions;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.Arguments;
24+
import org.junit.jupiter.params.provider.MethodSource;
25+
26+
import software.xdev.bzst.dip.client.ConfigurationTestUtil;
27+
import software.xdev.bzst.dip.client.model.configuration.BzstDipConfiguration;
28+
import software.xdev.bzst.dip.client.xmldocument.XMLDocumentBodyCreator;
29+
import software.xdev.bzst.dip.client.xmldocument.XMLDocumentCreator;
430

531

632
class XmlSignerTest
733
{
34+
private static Stream<Arguments> provideConfigurations()
35+
{
36+
return Stream.of(
37+
Arguments.of(ConfigurationTestUtil.getConfigurationWithJksSigning()),
38+
Arguments.of(ConfigurationTestUtil.getConfigurationWithPemSigning())
39+
);
40+
}
841

9-
@Test
10-
void signXMLDocument()
42+
@ParameterizedTest
43+
@MethodSource("provideConfigurations")
44+
void signXMLDocument(final BzstDipConfiguration configuration)
1145
{
12-
// TODO
46+
final XMLDocumentCreator xmlDocumentCreator = new XMLDocumentCreator(configuration);
47+
final String unsignedXml = xmlDocumentCreator.buildXMLDocument(
48+
List.of(),
49+
XMLDocumentBodyCreator.createPlatformOperatorFromConfiguration(configuration)
50+
);
51+
52+
final XmlSigner xmlSigner = new XmlSigner(configuration.getSigningProvider());
53+
final String signedXml = xmlSigner.signXMLDocument(unsignedXml);
54+
55+
Assertions.assertNotNull(signedXml);
56+
Assertions.assertTrue(signedXml.contains("SignedInfo"));
57+
Assertions.assertTrue(signedXml.contains("SignatureValue"));
58+
Assertions.assertTrue(signedXml.contains(
59+
"<ds:X509SubjectName>CN=xdev.software,O=XDEV Software GmbH,L=city,ST=Bavaria,C=DE</ds:X509SubjectName>"));
60+
Assertions.assertTrue(signedXml.contains("X509Certificate"));
1361
}
1462
}

0 commit comments

Comments
 (0)