SslCredentialsTest.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #region Copyright notice and license
  2. // Copyright 2015-2016 gRPC authors.
  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. #endregion
  16. using System;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Threading;
  21. using System.Threading.Tasks;
  22. using Google.Protobuf;
  23. using Grpc.Core;
  24. using Grpc.Core.Utils;
  25. using Grpc.Testing;
  26. using NUnit.Framework;
  27. namespace Grpc.IntegrationTesting
  28. {
  29. /// <summary>
  30. /// Test SSL credentials where server authenticates client
  31. /// and client authenticates the server.
  32. /// </summary>
  33. public class SslCredentialsTest
  34. {
  35. const string Host = "localhost";
  36. const string IsPeerAuthenticatedMetadataKey = "test_only_is_peer_authenticated";
  37. Server server;
  38. Channel channel;
  39. TestService.TestServiceClient client;
  40. string rootCert;
  41. KeyCertificatePair keyCertPair;
  42. public void InitClientAndServer(bool clientAddKeyCertPair,
  43. SslClientCertificateRequestType clientCertRequestType)
  44. {
  45. rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath);
  46. keyCertPair = new KeyCertificatePair(
  47. File.ReadAllText(TestCredentials.ServerCertChainPath),
  48. File.ReadAllText(TestCredentials.ServerPrivateKeyPath));
  49. var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType);
  50. var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair) : new SslCredentials(rootCert);
  51. // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
  52. server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
  53. {
  54. Services = { TestService.BindService(new SslCredentialsTestServiceImpl()) },
  55. Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
  56. };
  57. server.Start();
  58. var options = new List<ChannelOption>
  59. {
  60. new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
  61. };
  62. channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
  63. client = new TestService.TestServiceClient(channel);
  64. }
  65. [OneTimeTearDown]
  66. public void Cleanup()
  67. {
  68. if (channel != null)
  69. {
  70. channel.ShutdownAsync().Wait();
  71. }
  72. if (server != null)
  73. {
  74. server.ShutdownAsync().Wait();
  75. }
  76. }
  77. [Test]
  78. public async Task NoClientCert_DontRequestClientCertificate_Accepted()
  79. {
  80. InitClientAndServer(
  81. clientAddKeyCertPair: false,
  82. clientCertRequestType: SslClientCertificateRequestType.DontRequest);
  83. await CheckAccepted(expectPeerAuthenticated: false);
  84. }
  85. [Test]
  86. public async Task ClientWithCert_DontRequestClientCertificate_AcceptedButPeerNotAuthenticated()
  87. {
  88. InitClientAndServer(
  89. clientAddKeyCertPair: true,
  90. clientCertRequestType: SslClientCertificateRequestType.DontRequest);
  91. await CheckAccepted(expectPeerAuthenticated: false);
  92. }
  93. [Test]
  94. public async Task NoClientCert_RequestClientCertificateButDontVerify_Accepted()
  95. {
  96. InitClientAndServer(
  97. clientAddKeyCertPair: false,
  98. clientCertRequestType: SslClientCertificateRequestType.RequestButDontVerify);
  99. await CheckAccepted(expectPeerAuthenticated: false);
  100. }
  101. [Test]
  102. public async Task NoClientCert_RequestClientCertificateAndVerify_Accepted()
  103. {
  104. InitClientAndServer(
  105. clientAddKeyCertPair: false,
  106. clientCertRequestType: SslClientCertificateRequestType.RequestAndVerify);
  107. await CheckAccepted(expectPeerAuthenticated: false);
  108. }
  109. [Test]
  110. public async Task ClientWithCert_RequestAndRequireClientCertificateButDontVerify_Accepted()
  111. {
  112. InitClientAndServer(
  113. clientAddKeyCertPair: true,
  114. clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireButDontVerify);
  115. await CheckAccepted(expectPeerAuthenticated: true);
  116. await CheckAuthContextIsPopulated();
  117. }
  118. [Test]
  119. public async Task ClientWithCert_RequestAndRequireClientCertificateAndVerify_Accepted()
  120. {
  121. InitClientAndServer(
  122. clientAddKeyCertPair: true,
  123. clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireAndVerify);
  124. await CheckAccepted(expectPeerAuthenticated: true);
  125. await CheckAuthContextIsPopulated();
  126. }
  127. [Test]
  128. public void NoClientCert_RequestAndRequireClientCertificateButDontVerify_Rejected()
  129. {
  130. InitClientAndServer(
  131. clientAddKeyCertPair: false,
  132. clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireButDontVerify);
  133. CheckRejected();
  134. }
  135. [Test]
  136. public void NoClientCert_RequestAndRequireClientCertificateAndVerify_Rejected()
  137. {
  138. InitClientAndServer(
  139. clientAddKeyCertPair: false,
  140. clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireAndVerify);
  141. CheckRejected();
  142. }
  143. [Test]
  144. public void Constructor_LegacyForceClientAuth()
  145. {
  146. var creds = new SslServerCredentials(new[] { keyCertPair }, rootCert, true);
  147. Assert.AreEqual(SslClientCertificateRequestType.RequestAndRequireAndVerify, creds.ClientCertificateRequest);
  148. var creds2 = new SslServerCredentials(new[] { keyCertPair }, rootCert, false);
  149. Assert.AreEqual(SslClientCertificateRequestType.DontRequest, creds2.ClientCertificateRequest);
  150. }
  151. [Test]
  152. public void Constructor_NullRootCerts()
  153. {
  154. var keyCertPairs = new[] { keyCertPair };
  155. Assert.DoesNotThrow(() => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.DontRequest));
  156. Assert.DoesNotThrow(() => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndVerify));
  157. Assert.DoesNotThrow(() => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireButDontVerify));
  158. Assert.Throws(typeof(ArgumentNullException), () => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireAndVerify));
  159. }
  160. private async Task CheckAccepted(bool expectPeerAuthenticated)
  161. {
  162. var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 });
  163. var response = await call;
  164. Assert.AreEqual(10, response.Payload.Body.Length);
  165. Assert.AreEqual(expectPeerAuthenticated.ToString(), call.GetTrailers().First((entry) => entry.Key == IsPeerAuthenticatedMetadataKey).Value);
  166. }
  167. private void CheckRejected()
  168. {
  169. var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { ResponseSize = 10 }));
  170. Assert.AreEqual(StatusCode.Unavailable, ex.Status.StatusCode);
  171. }
  172. private async Task CheckAuthContextIsPopulated()
  173. {
  174. var call = client.StreamingInputCall();
  175. await call.RequestStream.CompleteAsync();
  176. var response = await call.ResponseAsync;
  177. Assert.AreEqual(12345, response.AggregatedPayloadSize);
  178. }
  179. private class SslCredentialsTestServiceImpl : TestService.TestServiceBase
  180. {
  181. public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
  182. {
  183. context.ResponseTrailers.Add(IsPeerAuthenticatedMetadataKey, context.AuthContext.IsPeerAuthenticated.ToString());
  184. return Task.FromResult(new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) });
  185. }
  186. public override async Task<StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<StreamingInputCallRequest> requestStream, ServerCallContext context)
  187. {
  188. var authContext = context.AuthContext;
  189. await requestStream.ForEachAsync(request => TaskUtils.CompletedTask);
  190. Assert.IsTrue(authContext.IsPeerAuthenticated);
  191. Assert.AreEqual("x509_subject_alternative_name", authContext.PeerIdentityPropertyName);
  192. Assert.IsTrue(authContext.PeerIdentity.Count() > 0);
  193. Assert.AreEqual("ssl", authContext.FindPropertiesByName("transport_security_type").First().Value);
  194. return new StreamingInputCallResponse { AggregatedPayloadSize = 12345 };
  195. }
  196. private static Payload CreateZerosPayload(int size)
  197. {
  198. return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
  199. }
  200. }
  201. }
  202. }