1
+ using System . Net . Security ;
2
+ using System . Security . Cryptography ;
3
+ using System . Security . Cryptography . X509Certificates ;
1
4
using Moq ;
2
5
using NRedisStack . DataTypes ;
3
6
using NRedisStack . RedisStackCommands ;
4
7
using NRedisStack . Search ;
5
8
using NRedisStack . Search . Aggregation ;
6
9
using NRedisStack . Search . Literals . Enums ;
10
+ using Org . BouncyCastle . Crypto ;
11
+ using Org . BouncyCastle . Crypto . Parameters ;
12
+ using Org . BouncyCastle . Math ;
13
+ using Org . BouncyCastle . OpenSsl ;
7
14
using StackExchange . Redis ;
8
15
using Xunit ;
16
+ using Xunit . Abstractions ;
9
17
using static NRedisStack . Search . Schema ;
10
18
11
19
namespace NRedisStack . Tests ;
12
20
13
21
public class ExaplesTests : AbstractNRedisStackTest , IDisposable
14
22
{
23
+ private readonly ITestOutputHelper testOutputHelper ;
15
24
Mock < IDatabase > _mock = new Mock < IDatabase > ( ) ;
16
25
private readonly string key = "EXAMPLES_TESTS" ;
17
- public ExaplesTests ( RedisFixture redisFixture ) : base ( redisFixture ) { }
26
+ public ExaplesTests ( RedisFixture redisFixture , ITestOutputHelper testOutputHelper ) : base ( redisFixture )
27
+ {
28
+ this . testOutputHelper = testOutputHelper ;
29
+ }
18
30
19
31
public void Dispose ( )
20
32
{
@@ -292,6 +304,272 @@ public void TestJsonConvert()
292
304
Assert . Equal ( 10 , docs . Count ( ) ) ;
293
305
}
294
306
307
+ #if NET481
308
+ [ Fact ]
309
+ public void TestRedisCloudConnection_net481 ( )
310
+ {
311
+ var root = Path . GetFullPath ( Directory . GetCurrentDirectory ( ) ) ;
312
+ var redisCaPath = Path . GetFullPath ( Path . Combine ( root , "redis_ca.pem" ) ) ;
313
+ var redisUserCrtPath = Path . GetFullPath ( Path . Combine ( root , "redis_user.crt" ) ) ;
314
+ var redisUserPrivateKeyPath = Path . GetFullPath ( Path . Combine ( root , "redis_user_private.key" ) ) ;
315
+
316
+ var password = Environment . GetEnvironmentVariable ( "PASSWORD" ) ?? throw new Exception ( "PASSWORD is not set." ) ;
317
+ var endpoint = Environment . GetEnvironmentVariable ( "ENDPOINT" ) ?? throw new Exception ( "ENDPOINT is not set." ) ;
318
+
319
+ // Load the Redis credentials
320
+ var redisUserCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisUserCrtPath ) ) ;
321
+ var redisCaCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisCaPath ) ) ;
322
+
323
+ var rsa = RSA . Create ( ) ;
324
+
325
+ var redisUserPrivateKeyText = File . ReadAllText ( redisUserPrivateKeyPath ) . Trim ( ) ;
326
+ rsa . ImportParameters ( ImportPrivateKey ( redisUserPrivateKeyText ) ) ;
327
+
328
+ var clientCert = redisUserCertificate . CopyWithPrivateKey ( rsa ) ;
329
+
330
+ // Connect to Redis Cloud
331
+ var redisConfiguration = new ConfigurationOptions
332
+ {
333
+ EndPoints = { endpoint } ,
334
+ Ssl = true ,
335
+ Password = password
336
+ } ;
337
+
338
+ redisConfiguration . CertificateSelection += ( _ , _ , _ , _ , _ ) => new X509Certificate2 ( clientCert . Export ( X509ContentType . Pfx ) ) ;
339
+
340
+ redisConfiguration . CertificateValidation += ( _ , cert , _ , errors ) =>
341
+ {
342
+ if ( errors == SslPolicyErrors . None )
343
+ {
344
+ return true ;
345
+ }
346
+
347
+ var privateChain = new X509Chain ( ) ;
348
+ privateChain . ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode . NoCheck } ;
349
+ X509Certificate2 cert2 = new X509Certificate2 ( cert ! ) ;
350
+ privateChain . ChainPolicy . ExtraStore . Add ( redisCaCertificate ) ;
351
+ privateChain . Build ( cert2 ) ;
352
+
353
+ bool isValid = true ;
354
+
355
+ // we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
356
+ // matches our certificate, we know it's ok
357
+ foreach ( X509ChainStatus chainStatus in privateChain . ChainStatus . Where ( x =>
358
+ x . Status != X509ChainStatusFlags . UntrustedRoot ) )
359
+ {
360
+ if ( chainStatus . Status != X509ChainStatusFlags . NoError )
361
+ {
362
+ isValid = false ;
363
+ break ;
364
+ }
365
+ }
366
+
367
+ return isValid ;
368
+ } ;
369
+
370
+
371
+ var redis = ConnectionMultiplexer . Connect ( redisConfiguration ) ;
372
+ var db = redis . GetDatabase ( ) ;
373
+ db . Ping ( ) ;
374
+ }
375
+
376
+ public static RSAParameters ImportPrivateKey ( string pem )
377
+ {
378
+ using var sr = new StringReader ( pem ) ;
379
+ PemReader pr = new PemReader ( sr ) ;
380
+ RSAParameters rp = new RSAParameters ( ) ;
381
+ while ( sr . Peek ( ) != - 1 )
382
+ {
383
+ var privKey = pr . ReadObject ( ) as AsymmetricCipherKeyPair ;
384
+ if ( privKey != null )
385
+ {
386
+ var pkParamaters = ( RsaPrivateCrtKeyParameters ) privKey . Private ;
387
+ rp . Modulus = pkParamaters . Modulus . ToByteArrayUnsigned ( ) ;
388
+ rp . Exponent = pkParamaters . PublicExponent . ToByteArrayUnsigned ( ) ;
389
+ rp . P = pkParamaters . P . ToByteArrayUnsigned ( ) ;
390
+ rp . Q = pkParamaters . Q . ToByteArrayUnsigned ( ) ;
391
+ rp . D = ConvertRSAParametersField ( pkParamaters . Exponent , rp . Modulus . Length ) ;
392
+ rp . DP = ConvertRSAParametersField ( pkParamaters . DP , rp . P . Length ) ;
393
+ rp . DQ = ConvertRSAParametersField ( pkParamaters . DQ , rp . Q . Length ) ;
394
+ rp . InverseQ = ConvertRSAParametersField ( pkParamaters . QInv , rp . Q . Length ) ;
395
+ }
396
+ else
397
+ {
398
+ throw new ArgumentException ( "Pem is malformed and could not be parsed" ) ;
399
+ }
400
+ }
401
+ pr . ReadObject ( ) ;
402
+ return rp ;
403
+ }
404
+
405
+ private static byte [ ] ConvertRSAParametersField ( BigInteger n , int size )
406
+ {
407
+ byte [ ] bs = n . ToByteArrayUnsigned ( ) ;
408
+ if ( bs . Length == size )
409
+ return bs ;
410
+ if ( bs . Length > size )
411
+ throw new ArgumentException ( "Specified size too small" , "size" ) ;
412
+ byte [ ] padded = new byte [ size ] ;
413
+ Array . Copy ( bs , 0 , padded , size - bs . Length , bs . Length ) ;
414
+ return padded ;
415
+ }
416
+ #endif
417
+
418
+ #if NET6_0_OR_GREATER
419
+ [ Fact ]
420
+ public void TestRedisCloudConnection ( )
421
+ {
422
+ var root = Path . GetFullPath ( Directory . GetCurrentDirectory ( ) ) ;
423
+ var redisCaPath = Path . GetFullPath ( Path . Combine ( root , "redis_ca.pem" ) ) ;
424
+ var redisUserCrtPath = Path . GetFullPath ( Path . Combine ( root , "redis_user.crt" ) ) ;
425
+ var redisUserPrivateKeyPath = Path . GetFullPath ( Path . Combine ( root , "redis_user_private.key" ) ) ;
426
+
427
+ var password = Environment . GetEnvironmentVariable ( "PASSWORD" ) ?? throw new Exception ( "PASSWORD is not set." ) ;
428
+ var endpoint = Environment . GetEnvironmentVariable ( "ENDPOINT" ) ?? throw new Exception ( "ENDPOINT is not set." ) ;
429
+
430
+ // Load the Redis credentials
431
+ var redisUserCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisUserCrtPath ) ) ;
432
+ var redisCaCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisCaPath ) ) ;
433
+
434
+ var rsa = RSA . Create ( ) ;
435
+
436
+ var redisUserPrivateKeyText = File . ReadAllText ( redisUserPrivateKeyPath ) ;
437
+ var pemFileData = File . ReadAllLines ( redisUserPrivateKeyPath ) . Where ( x => ! x . StartsWith ( "-" ) ) ;
438
+ var binaryEncoding = Convert . FromBase64String ( string . Join ( null , pemFileData ) ) ;
439
+
440
+ rsa . ImportRSAPrivateKey ( binaryEncoding , out _ ) ;
441
+ redisUserCertificate . CopyWithPrivateKey ( rsa ) ;
442
+ rsa . ImportFromPem ( redisUserPrivateKeyText . ToCharArray ( ) ) ;
443
+ var clientCert = redisUserCertificate . CopyWithPrivateKey ( rsa ) ;
444
+
445
+ // Connect to Redis Cloud
446
+ var redisConfiguration = new ConfigurationOptions
447
+ {
448
+ EndPoints = { endpoint } ,
449
+ Ssl = true ,
450
+ Password = password
451
+ } ;
452
+
453
+ redisConfiguration . CertificateSelection += ( _ , _ , _ , _ , _ ) => clientCert ;
454
+
455
+ redisConfiguration . CertificateValidation += ( _ , cert , _ , errors ) =>
456
+ {
457
+ if ( errors == SslPolicyErrors . None )
458
+ {
459
+ return true ;
460
+ }
461
+
462
+ var privateChain = new X509Chain ( ) ;
463
+ privateChain . ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode . NoCheck } ;
464
+ X509Certificate2 cert2 = new X509Certificate2 ( cert ! ) ;
465
+ privateChain . ChainPolicy . ExtraStore . Add ( redisCaCertificate ) ;
466
+ privateChain . Build ( cert2 ) ;
467
+
468
+ bool isValid = true ;
469
+
470
+ // we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
471
+ // matches our certificate, we know it's ok
472
+ foreach ( X509ChainStatus chainStatus in privateChain . ChainStatus . Where ( x =>
473
+ x . Status != X509ChainStatusFlags . UntrustedRoot ) )
474
+ {
475
+ if ( chainStatus . Status != X509ChainStatusFlags . NoError )
476
+ {
477
+ isValid = false ;
478
+ break ;
479
+ }
480
+ }
481
+
482
+ return isValid ;
483
+ } ;
484
+
485
+
486
+ var redis = ConnectionMultiplexer . Connect ( redisConfiguration ) ;
487
+ var db = redis . GetDatabase ( ) ;
488
+ db . Ping ( ) ;
489
+ }
490
+
491
+ [ Fact ]
492
+ public void TestRedisCloudConnection_DotnetCore3 ( )
493
+ {
494
+ // Replace this with your own Redis Cloud credentials
495
+ var root = Path . GetFullPath ( Directory . GetCurrentDirectory ( ) ) ;
496
+ var redisCaPath = Path . GetFullPath ( Path . Combine ( root , "redis_ca.pem" ) ) ;
497
+ var redisUserCrtPath = Path . GetFullPath ( Path . Combine ( root , "redis_user.crt" ) ) ;
498
+ var redisUserPrivateKeyPath = Path . GetFullPath ( Path . Combine ( root , "redis_user_private.key" ) ) ;
499
+
500
+ var password = Environment . GetEnvironmentVariable ( "PASSWORD" ) ?? throw new Exception ( "PASSWORD is not set." ) ;
501
+ var endpoint = Environment . GetEnvironmentVariable ( "ENDPOINT" ) ?? throw new Exception ( "ENDPOINT is not set." ) ;
502
+
503
+ // Load the Redis credentials
504
+ var redisUserCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisUserCrtPath ) ) ;
505
+ var redisCaCertificate = new X509Certificate2 ( File . ReadAllBytes ( redisCaPath ) ) ;
506
+
507
+ var rsa = RSA . Create ( ) ;
508
+
509
+ var redisUserPrivateKeyText = File . ReadAllText ( redisUserPrivateKeyPath ) ;
510
+ var pemFileData = File . ReadAllLines ( redisUserPrivateKeyPath ) . Where ( x => ! x . StartsWith ( "-" ) ) ;
511
+ var binaryEncoding = Convert . FromBase64String ( string . Join ( null , pemFileData ) ) ;
512
+
513
+ rsa . ImportRSAPrivateKey ( binaryEncoding , out _ ) ;
514
+ redisUserCertificate . CopyWithPrivateKey ( rsa ) ;
515
+ rsa . ImportFromPem ( redisUserPrivateKeyText . ToCharArray ( ) ) ;
516
+ var clientCert = redisUserCertificate . CopyWithPrivateKey ( rsa ) ;
517
+
518
+ var sslOptions = new SslClientAuthenticationOptions
519
+ {
520
+ CertificateRevocationCheckMode = X509RevocationMode . NoCheck ,
521
+ LocalCertificateSelectionCallback = ( _ , _ , _ , _ , _ ) => clientCert ,
522
+ RemoteCertificateValidationCallback = ( _ , cert , _ , errors ) =>
523
+ {
524
+ if ( errors == SslPolicyErrors . None )
525
+ {
526
+ return true ;
527
+ }
528
+
529
+ var privateChain = new X509Chain ( ) ;
530
+ privateChain . ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode . NoCheck } ;
531
+ X509Certificate2 cert2 = new X509Certificate2 ( cert ! ) ;
532
+ privateChain . ChainPolicy . ExtraStore . Add ( redisCaCertificate ) ;
533
+ privateChain . Build ( cert2 ) ;
534
+
535
+ bool isValid = true ;
536
+
537
+ // we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
538
+ // matches our certificate, we know it's ok
539
+ foreach ( X509ChainStatus chainStatus in privateChain . ChainStatus . Where ( x=> x . Status != X509ChainStatusFlags . UntrustedRoot ) )
540
+ {
541
+ if ( chainStatus . Status != X509ChainStatusFlags . NoError )
542
+ {
543
+ isValid = false ;
544
+ break ;
545
+ }
546
+ }
547
+
548
+ return isValid ;
549
+ } ,
550
+ TargetHost = endpoint
551
+ } ;
552
+ // Connect to Redis Cloud
553
+ var redisConfiguration = new ConfigurationOptions
554
+ {
555
+ EndPoints = { endpoint } ,
556
+ Ssl = true ,
557
+ SslHost = sslOptions . TargetHost ,
558
+ SslClientAuthenticationOptions = host => sslOptions ,
559
+ Password = password
560
+ } ;
561
+
562
+
563
+ var redis = ConnectionMultiplexer . Connect ( redisConfiguration ) ;
564
+ var db = redis . GetDatabase ( ) ;
565
+ db . Ping ( ) ;
566
+
567
+ db . StringSet ( "testKey" , "testValue" ) ;
568
+ var value = db . StringGet ( "testKey" ) ;
569
+ Assert . Equal ( "testValue" , value ) ;
570
+ }
571
+ #endif
572
+
295
573
[ Fact ]
296
574
public void BasicJsonExamplesTest ( )
297
575
{
@@ -1067,4 +1345,4 @@ private static void SortAndCompare(List<string> expectedList, List<string> res)
1067
1345
Assert . Equal ( expectedList [ i ] , res [ i ] . ToString ( ) ) ;
1068
1346
}
1069
1347
}
1070
- }
1348
+ }
0 commit comments