From da6a56cea0a9305cfa53e99c6c1f9da72735a1ac Mon Sep 17 00:00:00 2001 From: diego Date: Thu, 14 Nov 2024 19:39:33 +0100 Subject: [PATCH] [misc] parsec fingerprint hash using either java 15 or BouncyCastle --- pom.xml | 28 ++++++++++ .../jdbc/client/impl/StandardClient.java | 5 +- .../message/client/BulkExecutePacket.java | 2 +- .../standard/ParsecPasswordPlugin.java | 56 ++++++------------- .../standard/ParsecPasswordPluginTool.java | 25 +++++++++ .../tls/main/DefaultTlsSocketPlugin.java | 1 - .../standard/ParsecPasswordPluginTool.java | 43 ++++++++++++++ src/main/java9/module-info.java | 2 + .../org/mariadb/jdbc/integration/SslTest.java | 2 +- 9 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java create mode 100644 src/main/java15/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java diff --git a/pom.xml b/pom.xml index 873fc0f0b..c77338e90 100644 --- a/pom.xml +++ b/pom.xml @@ -281,6 +281,18 @@ 2.0.3 true + + + org.bouncycastle + bcpkix-jdk18on + 1.78.1 + + + + + + + org.osgi @@ -413,6 +425,22 @@ true + + compile-java-15 + compile + + compile + + + 15 + 15 + 15 + + ${project.basedir}/src/main/java15 + + true + + diff --git a/src/main/java/org/mariadb/jdbc/client/impl/StandardClient.java b/src/main/java/org/mariadb/jdbc/client/impl/StandardClient.java index 16f54d767..32aa74466 100644 --- a/src/main/java/org/mariadb/jdbc/client/impl/StandardClient.java +++ b/src/main/java/org/mariadb/jdbc/client/impl/StandardClient.java @@ -337,8 +337,9 @@ public void authenticationHandler(Credential credential, HostAddress hostAddress throw context .getExceptionFactory() .create( - "Self signed certificates. Either set sslMode=trust, use password with a MitM-Proof authentication plugin or" - + " provide server certificate to client", + "Self signed certificates. Either set sslMode=trust, use password with a" + + " MitM-Proof authentication plugin or provide server certificate to" + + " client", "08000"); } } diff --git a/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java b/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java index 7c849a4c3..bbd07cf86 100644 --- a/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java +++ b/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java @@ -206,7 +206,7 @@ public boolean binaryProtocol() { } public String description() { - return "BULK: " +command; + return "BULK: " + command; } public void setPrepareResult(PrepareResultPacket prepareResult) { diff --git a/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPlugin.java b/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPlugin.java index 4b1eaa824..32623a906 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPlugin.java +++ b/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPlugin.java @@ -7,7 +7,6 @@ import java.security.*; import java.security.spec.*; import java.sql.SQLException; -import java.util.Arrays; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; @@ -22,10 +21,10 @@ public class ParsecPasswordPlugin implements AuthenticationPlugin { private static byte[] pkcs8Ed25519header = - new byte[] { - 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, - 0x20 - }; + new byte[] { + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, + 0x20 + }; private String authenticationData; private byte[] seed; private byte[] salt; @@ -55,7 +54,7 @@ public ParsecPasswordPlugin(String authenticationData, byte[] seed) { * @throws IOException if socket error */ public ReadableByteBuf process(Writer out, Reader in, Context context) - throws SQLException, IOException { + throws SQLException, IOException { // request ext-salt out.writeEmptyPacket(); @@ -77,7 +76,7 @@ public ReadableByteBuf process(Writer out, Reader in, Context context) salt = new byte[buf.readableBytes()]; buf.readBytes(salt); char[] password = - this.authenticationData == null ? new char[0] : this.authenticationData.toCharArray(); + this.authenticationData == null ? new char[0] : this.authenticationData.toCharArray(); KeyFactory ed25519KeyFactory; Signature ed25519Signature; @@ -93,9 +92,9 @@ public ReadableByteBuf process(Writer out, Reader in, Context context) ed25519Signature = Signature.getInstance("Ed25519", "BC"); } catch (NoSuchAlgorithmException | NoSuchProviderException ee) { throw new SQLException( - "Parsec authentication not available. Either use Java 15+ or add BouncyCastle" - + " dependency", - e); + "Parsec authentication not available. Either use Java 15+ or add BouncyCastle" + + " dependency", + e); } } @@ -107,23 +106,14 @@ public ReadableByteBuf process(Writer out, Reader in, Context context) // create a PKCS8 ED25519 private key with raw secret PKCS8EncodedKeySpec keySpec = - new PKCS8EncodedKeySpec(combineArray(pkcs8Ed25519header, derivedKey)); + new PKCS8EncodedKeySpec(combineArray(pkcs8Ed25519header, derivedKey)); PrivateKey privateKey = ed25519KeyFactory.generatePrivate(keySpec); - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519"); - keyPairGenerator.initialize( - new NamedParameterSpec("Ed25519"), new StaticSecureRandom(derivedKey)); - byte[] spki = - keyPairGenerator - .generateKeyPair() - .getPublic() - .getEncoded(); // public key in SPKI format; the last 32 bytes are the raw public key - byte[] rawPublicKey = - Arrays.copyOfRange(spki, spki.length - 32, spki.length); // 32 bytes raw public key + byte[] rawPublicKey = ParsecPasswordPluginTool.process(derivedKey); hash = - combineArray( - combineArray(new byte[] {(byte) 'P', (byte) iterations}, salt), rawPublicKey); + combineArray( + combineArray(new byte[] {(byte) 'P', (byte) iterations}, salt), rawPublicKey); // generate client nonce byte[] clientScramble = new byte[32]; @@ -143,27 +133,15 @@ public ReadableByteBuf process(Writer out, Reader in, Context context) return in.readReusablePacket(); } catch (NoSuchAlgorithmException - | InvalidKeySpecException - | InvalidKeyException - | InvalidAlgorithmParameterException - | SignatureException e) { + | InvalidKeySpecException + | InvalidKeyException + | InvalidAlgorithmParameterException + | SignatureException e) { // not expected throw new SQLException("Error during parsec authentication", e); } } - private class StaticSecureRandom extends SecureRandom { - private byte[] privateKey; - - public StaticSecureRandom(byte[] privateKey) { - this.privateKey = privateKey; - } - - public void nextBytes(byte[] bytes) { - System.arraycopy(privateKey, 0, bytes, 0, privateKey.length); - } - } - public boolean isMitMProof() { return true; } diff --git a/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java b/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java new file mode 100644 index 000000000..b2dc6dc72 --- /dev/null +++ b/src/main/java/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (c) 2012-2014 Monty Program Ab +// Copyright (c) 2015-2024 MariaDB Corporation Ab +package org.mariadb.jdbc.plugin.authentication.standard; + +import java.io.IOException; +import java.security.*; +import java.sql.SQLException; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; + +/** Parsec password plugin utility */ +public class ParsecPasswordPluginTool { + + public static byte[] process(byte[] rawPrivateKey) + throws SQLException, + IOException, + InvalidAlgorithmParameterException, + NoSuchAlgorithmException { + Ed25519PrivateKeyParameters privateKeyRebuild = + new Ed25519PrivateKeyParameters(rawPrivateKey, 0); + Ed25519PublicKeyParameters publicKeyRebuild = privateKeyRebuild.generatePublicKey(); + return publicKeyRebuild.getEncoded(); + } +} diff --git a/src/main/java/org/mariadb/jdbc/plugin/tls/main/DefaultTlsSocketPlugin.java b/src/main/java/org/mariadb/jdbc/plugin/tls/main/DefaultTlsSocketPlugin.java index 23dfdc577..6e1722cdb 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/tls/main/DefaultTlsSocketPlugin.java +++ b/src/main/java/org/mariadb/jdbc/plugin/tls/main/DefaultTlsSocketPlugin.java @@ -5,7 +5,6 @@ import java.io.*; import java.net.URI; -import java.net.URL; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; diff --git a/src/main/java15/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java b/src/main/java15/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java new file mode 100644 index 000000000..ad20976a1 --- /dev/null +++ b/src/main/java15/org/mariadb/jdbc/plugin/authentication/standard/ParsecPasswordPluginTool.java @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (c) 2012-2014 Monty Program Ab +// Copyright (c) 2015-2024 MariaDB Corporation Ab +package org.mariadb.jdbc.plugin.authentication.standard; + +import java.io.IOException; +import java.security.*; +import java.security.spec.NamedParameterSpec; +import java.sql.SQLException; +import java.util.Arrays; + +/** Parsec password plugin utility*/ +public class ParsecPasswordPluginTool { + + public static byte[] process(byte[] rawPrivateKey) + throws SQLException, IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519"); + keyPairGenerator.initialize(NamedParameterSpec.ED25519, new StaticSecureRandom(rawPrivateKey)); + + // public key in SPKI format; the last 32 bytes are the raw public key + byte[] spki = + keyPairGenerator + .generateKeyPair() + .getPublic() + .getEncoded(); + byte[] rawPublicKey = + Arrays.copyOfRange(spki, spki.length - 32, spki.length); + return rawPublicKey; + } + + private static class StaticSecureRandom extends SecureRandom { + private byte[] privateKey; + + public StaticSecureRandom(byte[] privateKey) { + this.privateKey = privateKey; + } + + public void nextBytes(byte[] bytes) { + System.arraycopy(privateKey, 0, bytes, 0, privateKey.length); + } + } +} diff --git a/src/main/java9/module-info.java b/src/main/java9/module-info.java index 5f51b8977..ecaad2b34 100644 --- a/src/main/java9/module-info.java +++ b/src/main/java9/module-info.java @@ -6,6 +6,7 @@ requires transitive java.naming; requires transitive java.security.jgss; requires transitive jdk.net; + requires static waffle.jna; requires static software.amazon.awssdk.services.rds; requires static software.amazon.awssdk.regions; @@ -13,6 +14,7 @@ requires static com.sun.jna; requires static com.sun.jna.platform; requires static org.slf4j; + requires static org.bouncycastle.pkix; exports org.mariadb.jdbc; exports org.mariadb.jdbc.client; diff --git a/src/test/java/org/mariadb/jdbc/integration/SslTest.java b/src/test/java/org/mariadb/jdbc/integration/SslTest.java index 1e6e6171d..26cb5bf43 100644 --- a/src/test/java/org/mariadb/jdbc/integration/SslTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/SslTest.java @@ -615,7 +615,7 @@ public void trustStoreParameter() throws Throwable { if (caCertPath != null) { try (InputStream inStream2 = new File(caCertPath).toURI().toURL().openStream()) { CertificateFactory cf2 = CertificateFactory.getInstance("X.509"); - Collection caCertList = cf.generateCertificates(inStream); + Collection caCertList = cf2.generateCertificates(inStream2); for (Iterator iter = caCertList.iterator(); iter.hasNext(); ) { certs.add(iter.next()); }