This is an automated email from the ASF dual-hosted git repository. chrisdutz pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 3f383bca3a21384f77a3b9ff2ea43d7bcaa83af1 Author: Christofer Dutz <[email protected]> AuthorDate: Fri Aug 14 17:38:25 2026 +0200 chore(plc4x/opcua): Cover 4096 bit client certificates in the driver testsuite Several reports blamed connection failures on large certificates, so the size is now covered explicitly instead of only at the 2048 bits the rest of the suite uses: a second client identity with a 4096 bit key connects across every secured security policy and reads a value. These pass, so the key size alone does not break the handshake. The remaining suspect in those reports is the encoding of the reporters' certificates - #2196 shows the server failing to parse the client certificate's SubjectPublicKeyInfo, which is what an RSASSA-PSS typed key looks like to a Java parser. Relates to: #2013, #2196, #2286 --- .../plc4x/java/opcua/OpcuaPlcDriverTest.java | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java index 344880c841..371107f9a2 100644 --- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java @@ -84,9 +84,13 @@ public class OpcuaPlcDriverTest { private static final String APPLICATION_URI = "urn:org.apache:plc4x"; private static final KeystoreGenerator SERVER_KEY_STORE_GENERATOR = new KeystoreGenerator("password", 2048, APPLICATION_URI); private static final KeystoreGenerator CLIENT_KEY_STORE_GENERATOR = new KeystoreGenerator("changeit", 2048, APPLICATION_URI, "plc4x_plus_milo", "client"); + // A second client identity with a 4096-bit key. Several reports blamed connection failures on + // large certificates (GH-2013, GH-2196, GH-2286), so the size is covered explicitly. + private static final KeystoreGenerator CLIENT_KEY_STORE_4096_GENERATOR = new KeystoreGenerator("changeit", 4096, APPLICATION_URI, "plc4x_plus_milo", "client4096"); private static final File SECURITY_DIR; private static final File CLIENT_KEY_STORE; + private static final File CLIENT_KEY_STORE_4096; // The server's certificate, exported so the client can pin trust to it. Since the // driver now rejects server certificates by default (no permissive fallback), every // secured connection needs an explicit trust anchor. @@ -126,6 +130,15 @@ public class OpcuaPlcDriverTest { try (FileOutputStream outputStream = new FileOutputStream(new File(trustedCerts, "plc4x.crt"))) { CLIENT_KEY_STORE_GENERATOR.writeCertificateTo(outputStream); } + + // the same, for the 4096-bit client identity + CLIENT_KEY_STORE_4096 = Files.createTempFile("plc4x_opcua_client_4096_", ".p12").toAbsolutePath().toFile(); + try (FileOutputStream outputStream = new FileOutputStream(CLIENT_KEY_STORE_4096)) { + CLIENT_KEY_STORE_4096_GENERATOR.writeKeyStoreTo(outputStream); + } + try (FileOutputStream outputStream = new FileOutputStream(new File(trustedCerts, "plc4x_4096.crt"))) { + CLIENT_KEY_STORE_4096_GENERATOR.writeCertificateTo(outputStream); + } } catch (Exception e) { throw new ExceptionInInitializerError(e); } @@ -952,6 +965,38 @@ public class OpcuaPlcDriverTest { } } + /** + * Connecting with a 4096-bit client certificate. Several reports suspected large certificates + * of breaking the secure-channel handshake (GH-2013, GH-2196, GH-2286), so the key size is + * covered explicitly rather than only at the 2048 bits the rest of the suite uses. + */ + @Nested + class LargeCertificates { + + @ParameterizedTest + @MethodSource("org.apache.plc4x.java.opcua.OpcuaPlcDriverTest#getSecuredConnectionSecurityPolicies") + public void connectsWith4096BitClientCertificate(SecurityPolicy policy, MessageSecurity messageSecurity) throws Exception { + String connectionString = tcpConnectionAddress + PARAM_DIVIDER + params( + entry("key-store-file", CLIENT_KEY_STORE_4096.getAbsoluteFile().toString().replace("\\", "/")), + entry("key-store-password", "changeit"), + entry("key-store-type", "pkcs12"), + entry("server-certificate-file", SERVER_CERTIFICATE.toString().replace("\\", "/")), + entry("security-policy", policy.name()), + entry("message-security", messageSecurity.name())); + + try (PlcConnection connection = new DefaultPlcDriverManager().getConnection(connectionString)) { + assertThat(connection.isConnected()) + .describedAs("4096-bit client certificate with %s/%s", policy, messageSecurity) + .isTrue(); + + PlcReadResponse response = connection.readRequestBuilder() + .addTagAddress("value", BOOL_IDENTIFIER_READ_WRITE) + .build().execute().get(30, TimeUnit.SECONDS); + assertThat(response.getResponseCode("value")).isEqualTo(PlcResponseCode.OK); + } + } + } + /** * Username/password authentication against the Milo test server, which accepts * {@code user}/{@code password1} and {@code admin}/{@code password2}. @@ -1264,6 +1309,12 @@ public class OpcuaPlcDriverTest { } } + /** The secured policies only - an unsecured channel never exchanges certificates. */ + private static Stream<Arguments> getSecuredConnectionSecurityPolicies() { + return getConnectionSecurityPolicies() + .filter(arguments -> arguments.get()[0] != SecurityPolicy.NONE); + } + private static Stream<Arguments> getConnectionSecurityPolicies() { return Stream.of( Arguments.of(SecurityPolicy.NONE, MessageSecurity.NONE),
