Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita14da54

Browse files
svats0001jchrys
authored andcommitted
Added RSA encryption feature
Signed-off-by: S V <vats02581@gmail.com>
1 parent8b47ca5 commita14da54

File tree

9 files changed

+225
-12
lines changed

9 files changed

+225
-12
lines changed

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/InitFlow.java‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,18 +126,21 @@ final class InitFlow {
126126
* @param password the password of the {@code user}.
127127
* @param compressionAlgorithms the list of compression algorithms.
128128
* @param zstdCompressionLevel the zstd compression level.
129+
* @param serverRSAPublicKeyFile the local file path of the MySQL server's public key
129130
* @return a {@link Mono} that indicates the initialization is done, or an error if the initialization failed.
130131
*/
131132
staticMono<Void>initHandshake(Clientclient,SslModesslMode,Stringdatabase,Stringuser,
132-
@NullableCharSequencepassword,Set<CompressionAlgorithm>compressionAlgorithms,intzstdCompressionLevel) {
133+
@NullableCharSequencepassword,Set<CompressionAlgorithm>compressionAlgorithms,intzstdCompressionLevel,
134+
@NullableStringserverRSAPublicKeyFile) {
133135
returnclient.exchange(newHandshakeExchangeable(
134136
client,
135137
sslMode,
136138
database,
137139
user,
138140
password,
139141
compressionAlgorithms,
140-
zstdCompressionLevel
142+
zstdCompressionLevel,
143+
serverRSAPublicKeyFile
141144
)).then();
142145
}
143146

@@ -511,9 +514,12 @@ final class HandshakeExchangeable extends FluxExchangeable<Void> {
511514

512515
privatebooleansslCompleted;
513516

517+
@Nullable
518+
privateStringserverRSAPublicKeyFile;
519+
514520
HandshakeExchangeable(Clientclient,SslModesslMode,Stringdatabase,Stringuser,
515521
@NullableCharSequencepassword,Set<CompressionAlgorithm>compressions,
516-
intzstdCompressionLevel) {
522+
intzstdCompressionLevel,@NullableStringserverRSAPublicKeyFile) {
517523
this.client =client;
518524
this.sslMode =sslMode;
519525
this.database =database;
@@ -522,6 +528,7 @@ final class HandshakeExchangeable extends FluxExchangeable<Void> {
522528
this.compressions =compressions;
523529
this.zstdCompressionLevel =zstdCompressionLevel;
524530
this.sslCompleted =sslMode ==SslMode.TUNNEL;
531+
this.serverRSAPublicKeyFile =serverRSAPublicKeyFile;
525532
}
526533

527534
@Override
@@ -605,6 +612,11 @@ private AuthResponse createAuthResponse(String phase) {
605612
MySqlAuthProviderauthProvider =getAndNextProvider();
606613

607614
if (authProvider.isSslNecessary() && !sslCompleted) {
615+
if (serverRSAPublicKeyFile !=null &&sslMode.equals(SslMode.DISABLED)) {
616+
returnnewAuthResponse(MySqlAuthProvider.rsaEncryption(authProvider.authentication(
617+
password,salt,client.getContext().getClientCollation()),serverRSAPublicKeyFile,
618+
client.getContext().getServerVersion(),salt));
619+
}
608620
thrownewR2dbcPermissionDeniedException(authFails(authProvider.getType(),phase),CLI_SPECIFIC);
609621
}
610622

@@ -709,12 +721,17 @@ private MySqlAuthProvider getAndNextProvider() {
709721
privateHandshakeResponsecreateHandshakeResponse(Capabilitycapability) {
710722
MySqlAuthProviderauthProvider =getAndNextProvider();
711723

712-
if (authProvider.isSslNecessary() && !sslCompleted) {
713-
thrownewR2dbcPermissionDeniedException(authFails(authProvider.getType(),"handshake"),
714-
CLI_SPECIFIC);
724+
if (authProvider.isSslNecessary() && !sslCompleted &&serverRSAPublicKeyFile ==null) {
725+
thrownewR2dbcPermissionDeniedException(authFails(authProvider.getType(),"handshake"),CLI_SPECIFIC);
715726
}
716727

717728
byte[]authorization =authProvider.authentication(password,salt,client.getContext().getClientCollation());
729+
730+
if (authProvider.isSslNecessary() && !sslCompleted &&sslMode.equals(SslMode.DISABLED)) {
731+
authorization =MySqlAuthProvider.rsaEncryption(authorization,serverRSAPublicKeyFile,
732+
client.getContext().getServerVersion(),salt);
733+
}
734+
718735
StringauthType =authProvider.getType();
719736

720737
if (MySqlAuthProvider.NO_AUTH_PROVIDER.equals(authType)) {

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlConnectionConfiguration.java‎

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ public final class MySqlConnectionConfiguration {
136136

137137
privatefinalbooleantinyInt1isBit;
138138

139+
@Nullable
140+
privatefinalStringserverRSAPublicKeyFile;
141+
139142
privateMySqlConnectionConfiguration(
140143
booleanisHost,Stringdomain,intport,MySqlSslConfigurationssl,
141144
booleantcpKeepAlive,booleantcpNoDelay,@NullableDurationconnectTimeout,
@@ -153,7 +156,7 @@ private MySqlConnectionConfiguration(
153156
Extensionsextensions,@NullablePublisher<String>passwordPublisher,
154157
@NullableAddressResolverGroup<?>resolver,
155158
booleanmetrics,
156-
booleantinyInt1isBit) {
159+
booleantinyInt1isBit,@NullableStringserverRSAPublicKeyFile) {
157160
this.isHost =isHost;
158161
this.domain =domain;
159162
this.port =port;
@@ -185,6 +188,7 @@ private MySqlConnectionConfiguration(
185188
this.resolver =resolver;
186189
this.metrics =metrics;
187190
this.tinyInt1isBit =tinyInt1isBit;
191+
this.serverRSAPublicKeyFile =serverRSAPublicKeyFile;
188192
}
189193

190194
/**
@@ -328,6 +332,11 @@ boolean isTinyInt1isBit() {
328332
returntinyInt1isBit;
329333
}
330334

335+
@Nullable
336+
StringgetServerRSAPublicKeyFile() {
337+
returnserverRSAPublicKeyFile;
338+
}
339+
331340
@Override
332341
publicbooleanequals(Objecto) {
333342
if (this ==o) {
@@ -367,7 +376,8 @@ public boolean equals(Object o) {
367376
Objects.equals(passwordPublisher,that.passwordPublisher) &&
368377
Objects.equals(resolver,that.resolver) &&
369378
metrics ==that.metrics &&
370-
tinyInt1isBit ==that.tinyInt1isBit;
379+
tinyInt1isBit ==that.tinyInt1isBit &&
380+
Objects.equals(serverRSAPublicKeyFile,that.serverRSAPublicKeyFile);
371381
}
372382

373383
@Override
@@ -382,7 +392,8 @@ public int hashCode() {
382392
loadLocalInfilePath,localInfileBufferSize,
383393
queryCacheSize,prepareCacheSize,
384394
compressionAlgorithms,zstdCompressionLevel,
385-
loopResources,extensions,passwordPublisher,resolver,metrics,tinyInt1isBit);
395+
loopResources,extensions,passwordPublisher,resolver,metrics,tinyInt1isBit,
396+
serverRSAPublicKeyFile);
386397
}
387398

388399
@Override
@@ -418,7 +429,8 @@ private String buildCommonToStringPart() {
418429
", passwordPublisher=" +passwordPublisher +
419430
", resolver=" +resolver +
420431
", metrics=" +metrics +
421-
", tinyInt1isBit=" +tinyInt1isBit;
432+
", tinyInt1isBit=" +tinyInt1isBit +
433+
", serverRSAPublicKeyFile=" +serverRSAPublicKeyFile;
422434
}
423435

424436
/**
@@ -522,6 +534,9 @@ public static final class Builder {
522534

523535
privatebooleantinyInt1isBit =true;
524536

537+
@Nullable
538+
privateStringserverRSAPublicKeyFile;
539+
525540
/**
526541
* Builds an immutable {@link MySqlConnectionConfiguration} with current options.
527542
*
@@ -556,7 +571,8 @@ public MySqlConnectionConfiguration build() {
556571
loadLocalInfilePath,
557572
localInfileBufferSize,queryCacheSize,prepareCacheSize,
558573
compressionAlgorithms,zstdCompressionLevel,loopResources,
559-
Extensions.from(extensions,autodetectExtensions),passwordPublisher,resolver,metrics,tinyInt1isBit);
574+
Extensions.from(extensions,autodetectExtensions),passwordPublisher,resolver,metrics,tinyInt1isBit,
575+
serverRSAPublicKeyFile);
560576
}
561577

562578
/**
@@ -1234,6 +1250,21 @@ public Builder tinyInt1isBit(boolean tinyInt1isBit) {
12341250
returnthis;
12351251
}
12361252

1253+
/**
1254+
* Option to configure the database server's RSA Public Key file path on the local system if RSA encryption
1255+
* is desired such as when using caching_sha2_password authentication type while SSLMode is DISABLED. If
1256+
* serverRSAPublicKeyFile not null and SSLMode is not DISABLED, SSL encryption takes precedence.
1257+
*
1258+
* @param serverRSAPublicKeyFile the local file path of the database server's RSA Public Key file or
1259+
* {@code null} when RSA encryption not desired
1260+
* @return this {@link Builder}
1261+
* @since 1.4.2
1262+
*/
1263+
publicBuilderserverRSAPublicKeyFile(@NullableStringserverRSAPublicKeyFile) {
1264+
this.serverRSAPublicKeyFile =serverRSAPublicKeyFile;
1265+
returnthis;
1266+
}
1267+
12371268
privateSslModerequireSslMode() {
12381269
SslModesslMode =this.sslMode;
12391270

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlConnectionFactory.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ private static Mono<MySqlConnection> getMySqlConnection(
162162
user,
163163
password,
164164
configuration.getCompressionAlgorithms(),
165-
configuration.getZstdCompressionLevel()
165+
configuration.getZstdCompressionLevel(),
166+
configuration.getServerRSAPublicKeyFile()
166167
).then(InitFlow.initSession(
167168
client,
168169
sessionDb,

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlConnectionFactoryProvider.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ public final class MySqlConnectionFactoryProvider implements ConnectionFactoryPr
341341
*/
342342
publicstaticfinalOption<Boolean>TINY_INT_1_IS_BIT =Option.valueOf("tinyInt1isBit");
343343

344+
/**
345+
* Option to configure the database server's RSA Public Key file path on the local system if RSA encryption
346+
* is desired such as when using caching_sha2_password authentication type while
347+
* {@link io.asyncer.r2dbc.mysql.constant.SslMode} is DISABLED. If serverRSAPublicKeyFile not
348+
* {@code null} and {@link io.asyncer.r2dbc.mysql.constant.SslMode} is not DISABLED, SSL encryption
349+
* takes precedence.
350+
*
351+
* @since 1.4.2
352+
*/
353+
publicstaticfinalOption<String>SERVER_RSA_PUBLIC_KEY_FILE =Option.valueOf("serverRSAPublicKeyFile");
354+
344355
@Override
345356
publicConnectionFactorycreate(ConnectionFactoryOptionsoptions) {
346357
requireNonNull(options,"connectionFactoryOptions must not be null");
@@ -438,6 +449,8 @@ static MySqlConnectionConfiguration setup(ConnectionFactoryOptions options) {
438449
.to(builder::metrics);
439450
mapper.optional(TINY_INT_1_IS_BIT).asBoolean()
440451
.to(builder::tinyInt1isBit);
452+
mapper.optional(SERVER_RSA_PUBLIC_KEY_FILE).asString()
453+
.to(builder::serverRSAPublicKeyFile);
441454

442455
returnbuilder.build();
443456
}

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/authentication/AuthUtils.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,17 @@ private static byte[] allBytesXor(byte[] left, byte[] right) {
113113
returnleft;
114114
}
115115

116+
staticbyte[]rotatingXor(byte[]password,byte[]seedBytes) {
117+
intseedLength =seedBytes.length;
118+
intpasswordLength =password.length;
119+
byte[]buffer =newbyte[passwordLength];
120+
121+
for (inti =0;i <passwordLength;i++) {
122+
buffer[i] = (byte) (password[i] ^seedBytes[i %seedLength]);
123+
}
124+
125+
returnbuffer;
126+
}
127+
116128
privateAuthUtils() { }
117129
}

‎r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/authentication/MySqlAuthProvider.java‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,30 @@
1616

1717
packageio.asyncer.r2dbc.mysql.authentication;
1818

19+
importio.asyncer.r2dbc.mysql.ServerVersion;
1920
importio.asyncer.r2dbc.mysql.collation.CharCollation;
2021
importio.r2dbc.spi.R2dbcPermissionDeniedException;
2122
importorg.jetbrains.annotations.Nullable;
2223

2324
importstaticio.asyncer.r2dbc.mysql.internal.util.AssertUtils.requireNonNull;
2425

26+
importjava.io.IOException;
27+
importjava.nio.charset.Charset;
28+
importjava.nio.file.Files;
29+
importjava.nio.file.Paths;
30+
importjava.security.InvalidKeyException;
31+
importjava.security.KeyFactory;
32+
importjava.security.NoSuchAlgorithmException;
33+
importjava.security.interfaces.RSAPublicKey;
34+
importjava.security.spec.InvalidKeySpecException;
35+
importjava.security.spec.X509EncodedKeySpec;
36+
importjava.util.Base64;
37+
38+
importjavax.crypto.BadPaddingException;
39+
importjavax.crypto.Cipher;
40+
importjavax.crypto.IllegalBlockSizeException;
41+
importjavax.crypto.NoSuchPaddingException;
42+
2543
/**
2644
* An abstraction of the MySQL authorization plugin provider for connection phase. More information for MySQL
2745
* authentication type:
@@ -124,4 +142,57 @@ static MySqlAuthProvider build(String type) {
124142
* @return the next provider
125143
*/
126144
MySqlAuthProvidernext();
145+
146+
/**
147+
* Encrypts data with the RSA Public Key of MySQL server
148+
* @param bytesToEncrypt the data to encrypt
149+
* @param serverRSAPublicKeyFile the file path on the local system of the database server's RSA Public Key
150+
* @param serverVersion the version of the MySQL server
151+
* @param seed the seed bytes for rotating XOR obfuscation
152+
* @return the encrypted bytes
153+
*/
154+
staticbyte[]rsaEncryption(byte[]bytesToEncrypt,StringserverRsaPublicKeyFile,ServerVersionserverVersion,
155+
byte[]seed) {
156+
try {
157+
bytesToEncrypt =AuthUtils.rotatingXor(bytesToEncrypt,seed);
158+
159+
Stringkey =newString(Files.readAllBytes(Paths.get(serverRsaPublicKeyFile)),Charset.defaultCharset());
160+
161+
intstartIndex =key.indexOf("-----BEGIN PUBLIC KEY-----") +26;
162+
intendIndex =key.indexOf("-----END PUBLIC KEY-----");
163+
key =key.substring(startIndex,endIndex);
164+
StringpublicKeyPEM =key.replaceAll(System.lineSeparator(),"");
165+
166+
byte[]encoded =Base64.getDecoder().decode(publicKeyPEM);
167+
168+
KeyFactorykeyFactory =KeyFactory.getInstance("RSA");
169+
X509EncodedKeySpeckeySpec =newX509EncodedKeySpec(encoded);
170+
RSAPublicKeypk = (RSAPublicKey)keyFactory.generatePublic(keySpec);
171+
172+
Ciphercipher;
173+
if (serverVersion.isGreaterThanOrEqualTo(ServerVersion.create(8,0,5))) {
174+
cipher =Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
175+
}else {
176+
cipher =Cipher.getInstance("RSA/ECB/PKCS1Padding");
177+
}
178+
cipher.init(Cipher.ENCRYPT_MODE,pk);
179+
returncipher.doFinal(bytesToEncrypt);
180+
}catch (IOExceptione) {
181+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
182+
}catch (NoSuchAlgorithmExceptione) {
183+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
184+
}catch (InvalidKeySpecExceptione) {
185+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
186+
}catch (NoSuchPaddingExceptione) {
187+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
188+
}catch (InvalidKeyExceptione) {
189+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
190+
}catch (IllegalBlockSizeExceptione) {
191+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
192+
}catch (BadPaddingExceptione) {
193+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
194+
}catch (IndexOutOfBoundsExceptione) {
195+
thrownewIllegalArgumentException(e.getLocalizedMessage(),e);
196+
}
197+
}
127198
}

‎r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/MySqlConnectionConfigurationTest.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ private static MySqlConnectionConfiguration filledUp() {
282282
.lockWaitTimeout(Duration.ofSeconds(5))
283283
.statementTimeout(Duration.ofSeconds(10))
284284
.autodetectExtensions(false)
285+
.serverRSAPublicKeyFile("/path/to/mysql/serverRSAPublicKey.pem")
285286
.build();
286287
}
287288
}

‎r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/MySqlConnectionFactoryProviderTest.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
importstaticio.asyncer.r2dbc.mysql.MySqlConnectionFactoryProvider.METRICS;
5555
importstaticio.asyncer.r2dbc.mysql.MySqlConnectionFactoryProvider.PASSWORD_PUBLISHER;
5656
importstaticio.asyncer.r2dbc.mysql.MySqlConnectionFactoryProvider.RESOLVER;
57+
importstaticio.asyncer.r2dbc.mysql.MySqlConnectionFactoryProvider.SERVER_RSA_PUBLIC_KEY_FILE;
5758
importstaticio.asyncer.r2dbc.mysql.MySqlConnectionFactoryProvider.USE_SERVER_PREPARE_STATEMENT;
5859
importstaticio.r2dbc.spi.ConnectionFactoryOptions.CONNECT_TIMEOUT;
5960
importstaticio.r2dbc.spi.ConnectionFactoryOptions.DATABASE;
@@ -530,6 +531,18 @@ void sessionVariables(String input, List<String> expected) {
530531
assertThat(MySqlConnectionFactoryProvider.setup(options).getSessionVariables()).isEqualTo(expected);
531532
}
532533

534+
@Test
535+
voidvalidServerRSAPublicKeyFile() {
536+
ConnectionFactoryOptionsoptions =ConnectionFactoryOptions.builder()
537+
.option(DRIVER,"mysql")
538+
.option(HOST,"127.0.0.1")
539+
.option(USER,"root")
540+
.option(SERVER_RSA_PUBLIC_KEY_FILE,"/path/to/mysql/serverRSAPublicKey.pem")
541+
.build();
542+
543+
assertThat(MySqlConnectionFactoryProvider.setup(options).getServerRSAPublicKeyFile()).isEqualTo("/path/to/mysql/serverRSAPublicKey.pem");
544+
}
545+
533546
staticStream<Arguments>sessionVariables() {
534547
returnStream.of(
535548
Arguments.of("",Collections.emptyList()),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp