|
|
|
@ -37,20 +37,24 @@ namespace Grpc.IntegrationTesting |
|
|
|
|
public class SslCredentialsTest |
|
|
|
|
{ |
|
|
|
|
const string Host = "localhost"; |
|
|
|
|
const string IsPeerAuthenticatedMetadataKey = "test_only_is_peer_authenticated"; |
|
|
|
|
Server server; |
|
|
|
|
Channel channel; |
|
|
|
|
TestService.TestServiceClient client; |
|
|
|
|
|
|
|
|
|
[OneTimeSetUp] |
|
|
|
|
public void Init() |
|
|
|
|
string rootCert; |
|
|
|
|
KeyCertificatePair keyCertPair; |
|
|
|
|
|
|
|
|
|
public void InitClientAndServer(bool clientAddKeyCertPair, |
|
|
|
|
SslClientCertificateRequestType clientCertRequestType) |
|
|
|
|
{ |
|
|
|
|
var rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); |
|
|
|
|
var keyCertPair = new KeyCertificatePair( |
|
|
|
|
rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); |
|
|
|
|
keyCertPair = new KeyCertificatePair( |
|
|
|
|
File.ReadAllText(TestCredentials.ServerCertChainPath), |
|
|
|
|
File.ReadAllText(TestCredentials.ServerPrivateKeyPath)); |
|
|
|
|
|
|
|
|
|
var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true); |
|
|
|
|
var clientCredentials = new SslCredentials(rootCert, keyCertPair); |
|
|
|
|
var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType); |
|
|
|
|
var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair) : new SslCredentials(rootCert); |
|
|
|
|
|
|
|
|
|
// Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 |
|
|
|
|
server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) |
|
|
|
@ -72,19 +76,133 @@ namespace Grpc.IntegrationTesting |
|
|
|
|
[OneTimeTearDown] |
|
|
|
|
public void Cleanup() |
|
|
|
|
{ |
|
|
|
|
channel.ShutdownAsync().Wait(); |
|
|
|
|
server.ShutdownAsync().Wait(); |
|
|
|
|
if (channel != null) |
|
|
|
|
{ |
|
|
|
|
channel.ShutdownAsync().Wait(); |
|
|
|
|
} |
|
|
|
|
if (server != null) |
|
|
|
|
{ |
|
|
|
|
server.ShutdownAsync().Wait(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void AuthenticatedClientAndServer() |
|
|
|
|
public async Task NoClientCert_DontRequestClientCertificate_Accepted() |
|
|
|
|
{ |
|
|
|
|
var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); |
|
|
|
|
Assert.AreEqual(10, response.Payload.Body.Length); |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: false, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.DontRequestClientCertificate); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public async Task AuthContextIsPopulated() |
|
|
|
|
public async Task ClientWithCert_DontRequestClientCertificate_AcceptedButPeerNotAuthenticated() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: true, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.DontRequestClientCertificate); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public async Task NoClientCert_RequestClientCertificateButDontVerify_Accepted() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: false, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestClientCertificateButDontVerify); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public async Task NoClientCert_RequestClientCertificateAndVerify_Accepted() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: false, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestClientCertificateAndVerify); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public async Task ClientWithCert_RequestAndRequireClientCertificateButDontVerify_Accepted() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: true, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: true); |
|
|
|
|
await CheckAuthContextIsPopulated(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public async Task ClientWithCert_RequestAndRequireClientCertificateAndVerify_Accepted() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: true, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify); |
|
|
|
|
|
|
|
|
|
await CheckAccepted(expectPeerAuthenticated: true); |
|
|
|
|
await CheckAuthContextIsPopulated(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void NoClientCert_RequestAndRequireClientCertificateButDontVerify_Rejected() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: false, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify); |
|
|
|
|
|
|
|
|
|
CheckRejected(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void NoClientCert_RequestAndRequireClientCertificateAndVerify_Rejected() |
|
|
|
|
{ |
|
|
|
|
InitClientAndServer( |
|
|
|
|
clientAddKeyCertPair: false, |
|
|
|
|
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify); |
|
|
|
|
|
|
|
|
|
CheckRejected(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void Constructor_LegacyForceClientAuth() |
|
|
|
|
{ |
|
|
|
|
var creds = new SslServerCredentials(new[] { keyCertPair }, rootCert, true); |
|
|
|
|
Assert.AreEqual(SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify, creds.ClientCertificateRequest); |
|
|
|
|
|
|
|
|
|
var creds2 = new SslServerCredentials(new[] { keyCertPair }, rootCert, false); |
|
|
|
|
Assert.AreEqual(SslClientCertificateRequestType.DontRequestClientCertificate, creds2.ClientCertificateRequest); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void Constructor_NullRootCerts() |
|
|
|
|
{ |
|
|
|
|
var keyCertPairs = new[] { keyCertPair }; |
|
|
|
|
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.DontRequestClientCertificate); |
|
|
|
|
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestClientCertificateAndVerify); |
|
|
|
|
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify); |
|
|
|
|
Assert.Throws(typeof(ArgumentNullException), () => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private async Task CheckAccepted(bool expectPeerAuthenticated) |
|
|
|
|
{ |
|
|
|
|
var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 }); |
|
|
|
|
var response = await call; |
|
|
|
|
Assert.AreEqual(10, response.Payload.Body.Length); |
|
|
|
|
Assert.AreEqual(expectPeerAuthenticated.ToString(), call.GetTrailers().First((entry) => entry.Key == IsPeerAuthenticatedMetadataKey).Value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void CheckRejected() |
|
|
|
|
{ |
|
|
|
|
var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { ResponseSize = 10 })); |
|
|
|
|
Assert.AreEqual(StatusCode.Unavailable, ex.Status.StatusCode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private async Task CheckAuthContextIsPopulated() |
|
|
|
|
{ |
|
|
|
|
var call = client.StreamingInputCall(); |
|
|
|
|
await call.RequestStream.CompleteAsync(); |
|
|
|
@ -96,6 +214,7 @@ namespace Grpc.IntegrationTesting |
|
|
|
|
{ |
|
|
|
|
public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context) |
|
|
|
|
{ |
|
|
|
|
context.ResponseTrailers.Add(IsPeerAuthenticatedMetadataKey, context.AuthContext.IsPeerAuthenticated.ToString()); |
|
|
|
|
return Task.FromResult(new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|