From 8229f31f70ecdbe03d03c96cba17d6ee85397bca Mon Sep 17 00:00:00 2001 From: tjpcc Date: Fri, 20 Jan 2023 10:58:35 -0700 Subject: "tlsauth" contrib package This package adds authentication middlewares via TLS client certificates. --- contrib/cgi/cgi.go | 1 + contrib/tlsauth/approver.go | 17 ++++ contrib/tlsauth/approver_test.go | 47 +++++++++ contrib/tlsauth/auth.go | 46 +++++++++ contrib/tlsauth/auth_test.go | 182 +++++++++++++++++++++++++++++++++++ contrib/tlsauth/gemini.go | 58 +++++++++++ contrib/tlsauth/gemini_test.go | 111 +++++++++++++++++++++ contrib/tlsauth/testdata/client1.crt | 16 +++ contrib/tlsauth/testdata/client1.key | 28 ++++++ contrib/tlsauth/testdata/client2.crt | 17 ++++ contrib/tlsauth/testdata/client2.key | 28 ++++++ contrib/tlsauth/testdata/server.crt | 18 ++++ contrib/tlsauth/testdata/server.key | 27 ++++++ 13 files changed, 596 insertions(+) create mode 100644 contrib/tlsauth/approver.go create mode 100644 contrib/tlsauth/approver_test.go create mode 100644 contrib/tlsauth/auth.go create mode 100644 contrib/tlsauth/auth_test.go create mode 100644 contrib/tlsauth/gemini.go create mode 100644 contrib/tlsauth/gemini_test.go create mode 100644 contrib/tlsauth/testdata/client1.crt create mode 100644 contrib/tlsauth/testdata/client1.key create mode 100644 contrib/tlsauth/testdata/client2.crt create mode 100644 contrib/tlsauth/testdata/client2.key create mode 100644 contrib/tlsauth/testdata/server.crt create mode 100644 contrib/tlsauth/testdata/server.key (limited to 'contrib') diff --git a/contrib/cgi/cgi.go b/contrib/cgi/cgi.go index 7f88e57..6a420b3 100644 --- a/contrib/cgi/cgi.go +++ b/contrib/cgi/cgi.go @@ -177,6 +177,7 @@ func prepareCGIEnv( environ = append( environ, "TLS_CLIENT_HASH="+fingerprint(cert.Raw), + "TLS_CLIENT_CERT="+hex.EncodeToString(cert.Raw), "TLS_CLIENT_ISSUER="+cert.Issuer.String(), "TLS_CLIENT_ISSUER_CN="+cert.Issuer.CommonName, "TLS_CLIENT_SUBJECT="+cert.Subject.String(), diff --git a/contrib/tlsauth/approver.go b/contrib/tlsauth/approver.go new file mode 100644 index 0000000..064056d --- /dev/null +++ b/contrib/tlsauth/approver.go @@ -0,0 +1,17 @@ +package tlsauth + +import "crypto/x509" + +// Approver is a function that validates a certificate. +// +// It should not be have to handle a nil argument. +type Approver func(*x509.Certificate) bool + +// RequireSpecificIdentity builds an approver that demands one specific client certificate. +func RequireSpecificIdentity(identity *x509.Certificate) Approver { return identity.Equal } + +// Allow is an approver which permits anything. +func Allow(_ *x509.Certificate) bool { return true } + +// Reject is an approver which denies everything. +func Reject(_ *x509.Certificate) bool { return false } diff --git a/contrib/tlsauth/approver_test.go b/contrib/tlsauth/approver_test.go new file mode 100644 index 0000000..a2af838 --- /dev/null +++ b/contrib/tlsauth/approver_test.go @@ -0,0 +1,47 @@ +package tlsauth_test + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "tildegit.org/tjp/gus/contrib/tlsauth" +) + +func TestRequireSpecificIdentity(t *testing.T) { + cert1, err := leafCert("testdata/client1.crt", "testdata/client1.key") + assert.Nil(t, err) + + cert2, err := leafCert("testdata/client2.crt", "testdata/client2.key") + assert.Nil(t, err) + + assert.True(t, cert1.Equal(cert1)) + assert.False(t, cert1.Equal(cert2)) + assert.False(t, cert2.Equal(cert1)) + assert.True(t, cert2.Equal(cert2)) + + assert.True(t, tlsauth.RequireSpecificIdentity(cert1)(cert1)) + assert.False(t, tlsauth.RequireSpecificIdentity(cert1)(cert2)) + assert.False(t, tlsauth.RequireSpecificIdentity(cert2)(cert1)) + assert.True(t, tlsauth.RequireSpecificIdentity(cert2)(cert2)) +} + +func leafCert(certfile, keyfile string) (*x509.Certificate, error) { + cert, err := tls.LoadX509KeyPair(certfile, keyfile) + if err != nil { + return nil, err + } + + if cert.Leaf != nil { + return cert.Leaf, nil + } + + if len(cert.Certificate) == 0 { + return nil, errors.New("no certificate blocks found") + } + + return x509.ParseCertificate(cert.Certificate[0]) +} diff --git a/contrib/tlsauth/auth.go b/contrib/tlsauth/auth.go new file mode 100644 index 0000000..38ec3a3 --- /dev/null +++ b/contrib/tlsauth/auth.go @@ -0,0 +1,46 @@ +package tlsauth + +import ( + "context" + "crypto/x509" + + "tildegit.org/tjp/gus" +) + +// Identity returns the client certificate for the request or nil if there is none. +func Identity(request *gus.Request) *x509.Certificate { + if request.TLSState == nil || len(request.TLSState.PeerCertificates) == 0 { + return nil + } + return request.TLSState.PeerCertificates[0] +} + +// RequiredAuth produces an auth predicate. +// +// The check requires both that there is a client certificate associated with the +// request and that it passes the provided approver. +func RequiredAuth(approve Approver) func(context.Context, *gus.Request) bool { + return func(_ context.Context, request *gus.Request) bool { + identity := Identity(request) + if identity == nil { + return false + } + + return approve(identity) + } +} + +// OptionalAuth produces an auth predicate. +// +// The check allows through any request with no client certificate, but if +// there is one present then it requires that it pass the provided approver. +func OptionalAuth(approve Approver) func(context.Context, *gus.Request) bool { + return func(_ context.Context, request *gus.Request) bool { + identity := Identity(request) + if identity == nil { + return true + } + + return approve(identity) + } +} diff --git a/contrib/tlsauth/auth_test.go b/contrib/tlsauth/auth_test.go new file mode 100644 index 0000000..8361fc3 --- /dev/null +++ b/contrib/tlsauth/auth_test.go @@ -0,0 +1,182 @@ +package tlsauth_test + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "tildegit.org/tjp/gus" + "tildegit.org/tjp/gus/contrib/tlsauth" + "tildegit.org/tjp/gus/gemini" +) + +func TestIdentify(t *testing.T) { + invoked := false + + var leafCert *x509.Certificate + server, client, clientCert := setup(t, + "testdata/server.crt", "testdata/server.key", + "testdata/client1.crt", "testdata/client1.key", + func(_ context.Context, request *gus.Request) *gus.Response { + invoked = true + + ident := tlsauth.Identity(request) + if assert.NotNil(t, ident) { + assert.True(t, ident.Equal(leafCert)) + } + + return nil + }, + ) + leafCert, err := x509.ParseCertificate(clientCert.Certificate[0]) + require.Nil(t, err) + + go server.Serve() + defer server.Close() + + requestPath(t, client, server, "/") + assert.True(t, invoked) +} + +func TestRequiredAuth(t *testing.T) { + invoked1 := false + invoked2 := false + + handler1 := func(_ context.Context, request *gus.Request) *gus.Response { + invoked1 = true + return gemini.Success("", &bytes.Buffer{}) + } + + handler2 := func(_ context.Context, request *gus.Request) *gus.Response { + invoked2 = true + return gemini.Success("", &bytes.Buffer{}) + } + + authMiddleware := gus.Filter(tlsauth.RequiredAuth(tlsauth.Allow), nil) + + handler1 = gus.Filter( + func(_ context.Context, req *gus.Request) bool { + return strings.HasPrefix(req.Path, "/one") + }, + nil, + )(authMiddleware(handler1)) + handler2 = authMiddleware(handler2) + + server, client, _ := setup(t, + "testdata/server.crt", "testdata/server.key", + "testdata/client1.crt", "testdata/client1.key", + gus.FallthroughHandler(handler1, handler2), + ) + + go server.Serve() + defer server.Close() + + requestPath(t, client, server, "/one") + assert.True(t, invoked1) + + client, _ = clientFor(t, server, "", "") // no client cert this time + requestPath(t, client, server, "/two") + assert.False(t, invoked2) +} + +func TestOptionalAuth(t *testing.T) { + invoked1 := false + invoked2 := false + + handler1 := func(_ context.Context, request *gus.Request) *gus.Response { + if !strings.HasPrefix(request.Path, "/one") { + return nil + } + + invoked1 = true + return gemini.Success("", &bytes.Buffer{}) + } + + handler2 := func(_ context.Context, request *gus.Request) *gus.Response { + invoked2 = true + return gemini.Success("", &bytes.Buffer{}) + } + + mw := gus.Filter(tlsauth.OptionalAuth(tlsauth.Reject), nil) + handler := gus.FallthroughHandler(mw(handler1), mw(handler2)) + + server, client, _ := setup(t, + "testdata/server.crt", "testdata/server.key", + "testdata/client1.crt", "testdata/client1.key", + handler, + ) + + go server.Serve() + defer server.Close() + + requestPath(t, client, server, "/one") + assert.False(t, invoked1) + + client, _ = clientFor(t, server, "", "") + requestPath(t, client, server, "/two") + assert.True(t, invoked2) +} + +func setup( + t *testing.T, + serverCertPath string, + serverKeyPath string, + clientCertPath string, + clientKeyPath string, + handler gus.Handler, +) (gus.Server, gemini.Client, tls.Certificate) { + serverTLS, err := gemini.FileTLS(serverCertPath, serverKeyPath) + require.Nil(t, err) + + server, err := gemini.NewServer( + context.Background(), + serverTLS, + "tcp", + "127.0.0.1:0", + handler, + ) + require.Nil(t, err) + + client, clientCert := clientFor(t, server, clientCertPath, clientKeyPath) + + return server, client, clientCert +} + +func clientFor( + t *testing.T, + server gus.Server, + certPath string, + keyPath string, +) (gemini.Client, tls.Certificate) { + var clientCert tls.Certificate + var certs []tls.Certificate + if certPath != "" { + c, err := tls.LoadX509KeyPair(certPath, keyPath) + require.Nil(t, err) + + clientCert = c + certs = []tls.Certificate{c} + } + + return gemini.NewClient(&tls.Config{ + Certificates: certs, + InsecureSkipVerify: true, + }), clientCert +} + +func requestPath(t *testing.T, client gemini.Client, server gus.Server, path string) *gus.Response { + u, err := url.Parse("gemini://" + server.Address() + path) + require.Nil(t, err) + + response, err := client.RoundTrip(&gus.Request{URL: u}) + require.Nil(t, err) + + return response +} diff --git a/contrib/tlsauth/gemini.go b/contrib/tlsauth/gemini.go new file mode 100644 index 0000000..0db89de --- /dev/null +++ b/contrib/tlsauth/gemini.go @@ -0,0 +1,58 @@ +package tlsauth + +import ( + "context" + + "tildegit.org/tjp/gus" + "tildegit.org/tjp/gus/gemini" +) + +// GeminiAuth builds an authentication middleware from approval criteria. +// +// If a request does not contain a client certificate it will be rejected +// with a "60 certificate required" response. If the client identity does +// not pass the approver it will be rejected with "62 certificate invalid". +func GeminiAuth(approver Approver) gus.Middleware { + return func(inner gus.Handler) gus.Handler { + return func(ctx context.Context, request *gus.Request) *gus.Response { + identity := Identity(request) + if identity == nil { + return geminiMissingCert(ctx, request) + } + if !approver(identity) { + return geminiCertNotAuthorized(ctx, request) + } + + return inner(ctx, request) + } + } +} + +// GeminiOptionalAuth builds auth middleware which doesn't require an identity. +// +// If there is no client certificate the request will pass through the middleware. +// It will only be rejected with "62 certificate invalid" if there *is* a client +// certificate, but it fails the approval. +func GeminiOptionalAuth(approver Approver) gus.Middleware { + return func(inner gus.Handler) gus.Handler { + return func(ctx context.Context, request *gus.Request) *gus.Response { + identity := Identity(request) + if identity != nil && !approver(identity) { + return geminiCertNotAuthorized(ctx, request) + } + + return inner(ctx, request) + } + } +} + +// GeminiRequireCertificate is a middleware that only requires a client certificate. +var GeminiRequireCertificate = GeminiAuth(Allow) + +func geminiMissingCert(_ context.Context, _ *gus.Request) *gus.Response { + return gemini.RequireCert("A client certificate is required.") +} + +func geminiCertNotAuthorized(_ context.Context, _ *gus.Request) *gus.Response { + return gemini.CertAuthFailure("Client certificate not authorized.") +} diff --git a/contrib/tlsauth/gemini_test.go b/contrib/tlsauth/gemini_test.go new file mode 100644 index 0000000..bc87958 --- /dev/null +++ b/contrib/tlsauth/gemini_test.go @@ -0,0 +1,111 @@ +package tlsauth_test + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "tildegit.org/tjp/gus" + "tildegit.org/tjp/gus/contrib/tlsauth" + "tildegit.org/tjp/gus/gemini" +) + +func TestGeminiAuth(t *testing.T) { + handler1 := func(_ context.Context, request *gus.Request) *gus.Response { + if !strings.HasPrefix(request.Path, "/one") { + return nil + } + + return gemini.Success("", &bytes.Buffer{}) + } + handler2 := func(_ context.Context, request *gus.Request) *gus.Response { + if !strings.HasPrefix(request.Path, "/two") { + return nil + } + + return gemini.Success("", &bytes.Buffer{}) + } + handler3 := func(_ context.Context, request *gus.Request) *gus.Response { + if !strings.HasPrefix(request.Path, "/three") { + return nil + } + + return gemini.Success("", &bytes.Buffer{}) + } + handler4 := func(_ context.Context, request *gus.Request) *gus.Response { + return gemini.Success("", &bytes.Buffer{}) + } + + handler := gus.FallthroughHandler( + tlsauth.GeminiAuth(tlsauth.Allow)(handler1), + tlsauth.GeminiAuth(tlsauth.Allow)(handler2), + tlsauth.GeminiAuth(tlsauth.Reject)(handler3), + tlsauth.GeminiAuth(tlsauth.Reject)(handler4), + ) + + server, authClient, _ := setup(t, + "testdata/server.crt", "testdata/server.key", + "testdata/client1.crt", "testdata/client1.key", + handler, + ) + + authlessClient, _ := clientFor(t, server, "", "") + + go server.Serve() + defer server.Close() + + resp := requestPath(t, authClient, server, "/one") + assert.Equal(t, gemini.StatusSuccess, resp.Status) + + resp = requestPath(t, authlessClient, server, "/two") + assert.Equal(t, gemini.StatusClientCertificateRequired, resp.Status) + + resp = requestPath(t, authClient, server, "/three") + assert.Equal(t, gemini.StatusCertificateNotAuthorized, resp.Status) + + resp = requestPath(t, authlessClient, server, "/four") + assert.Equal(t, gemini.StatusClientCertificateRequired, resp.Status) +} + +func TestGeminiOptionalAuth(t *testing.T) { + pathHandler := func(path string) gus.Handler { + return func(_ context.Context, request *gus.Request) *gus.Response { + if !strings.HasPrefix(request.Path, path) { + return nil + } + return gemini.Success("", &bytes.Buffer{}) + } + } + + handler := gus.FallthroughHandler( + tlsauth.GeminiOptionalAuth(tlsauth.Allow)(pathHandler("/one")), + tlsauth.GeminiOptionalAuth(tlsauth.Allow)(pathHandler("/two")), + tlsauth.GeminiOptionalAuth(tlsauth.Reject)(pathHandler("/three")), + tlsauth.GeminiOptionalAuth(tlsauth.Reject)(pathHandler("/four")), + ) + + server, authClient, _ := setup(t, + "testdata/server.crt", "testdata/server.key", + "testdata/client1.crt", "testdata/client1.key", + handler, + ) + authlessClient, _ := clientFor(t, server, "", "") + + go server.Serve() + defer server.Close() + + resp := requestPath(t, authClient, server, "/one") + assert.Equal(t, gemini.StatusSuccess, resp.Status) + + resp = requestPath(t, authlessClient, server, "/two") + assert.Equal(t, gemini.StatusSuccess, resp.Status) + + resp = requestPath(t, authClient, server, "/three") + assert.Equal(t, gemini.StatusCertificateNotAuthorized, resp.Status) + + resp = requestPath(t, authlessClient, server, "/four") + assert.Equal(t, gemini.StatusSuccess, resp.Status) +} diff --git a/contrib/tlsauth/testdata/client1.crt b/contrib/tlsauth/testdata/client1.crt new file mode 100644 index 0000000..4398035 --- /dev/null +++ b/contrib/tlsauth/testdata/client1.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICnDCCAYQCAQAwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJdGVzdCB1c2Vy +MB4XDTIzMDEyMDAyMDkwNloXDTMzMDExOTA3MDAwMFowFDESMBAGA1UEAwwJdGVz +dCB1c2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3Ar3h421bfzG +DNvnZBWx63frF1ym93FqWaXA0qVTJYjnLcraTvNCzVt3zi8A9xgIQMy57Yy/QkQf +04TNj6oYPD7HumH1oblLsFIHk6ODtM5hv5RrGrWpKKS/SSb2HaKI+bjXaZCfYHNc +5ZKtH91c0wlRUYW8A4Cov6qydqv8jzWPYb3gIFsbkOLfdiZUdTFNg3hPF9mRNWv2 +OlAR7bkyrKCIcsYdOxDxmnZOZqJD9m92n/k/teb4yQnK1BY08HLzIUyb1jWw71ak +5keHvmGmxqvu8W6B5PJBrQwnfAqFnxGLbNZMr6lGHCJvjQiI2P6T39jb2LqOmOND +0zBFGJsLAwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAvyDck+fSTvALbf9AGpn9n +dIaAcKB/o+a9IHAex7ZNywoh1i41gnahuFSnjFbkruchtzG0blpt5/jcIU0Eqg2u +hR8ILLavVxdYSitpE/FTpmUQ70rieOy6kSp60P6fi7pRC47pnlPb9WOBtMvjCR5M +ATMClARo9m2tQVEh1k6ZFkgsPfoYCWxfPgBpCGMzFnXQNWiWN6DuB0xUVOugNncH +OiIDDOpFQuq9ZP/w9TVjgVHKJdet50F4moXdqXfeAitkKOIAJozCNwq0ayMfQDS5 +rRMUmlNUJA3qFZdmuyXSC/N+Rj/aQVcVFY1Y2KNlKfcArFgpDwUTPDfsoC9cBZBA +-----END CERTIFICATE----- diff --git a/contrib/tlsauth/testdata/client1.key b/contrib/tlsauth/testdata/client1.key new file mode 100644 index 0000000..684ca15 --- /dev/null +++ b/contrib/tlsauth/testdata/client1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcCveHjbVt/MYM +2+dkFbHrd+sXXKb3cWpZpcDSpVMliOctytpO80LNW3fOLwD3GAhAzLntjL9CRB/T +hM2Pqhg8Pse6YfWhuUuwUgeTo4O0zmG/lGsatakopL9JJvYdooj5uNdpkJ9gc1zl +kq0f3VzTCVFRhbwDgKi/qrJ2q/yPNY9hveAgWxuQ4t92JlR1MU2DeE8X2ZE1a/Y6 +UBHtuTKsoIhyxh07EPGadk5mokP2b3af+T+15vjJCcrUFjTwcvMhTJvWNbDvVqTm +R4e+YabGq+7xboHk8kGtDCd8CoWfEYts1kyvqUYcIm+NCIjY/pPf2NvYuo6Y40PT +MEUYmwsDAgMBAAECggEABUqeOTxHKKXzfUusfNOou6jelmk7+qdXj2BVCru/DCAG +rys5pLxk1ttkPikTNN33FNfXgMbpsoZA3a1L5DCK9Kft1aWVapYyI8NVO0+rUyXD +ZAAFs1a7AqczkmbFdGD8OkUfqQI5UvBzQ3ILh8CjAtAujG9S3iKx7CoGsKPiJu9v +XHXTa0ccKiJpqKvLDaUYUcfz+5DLVaFxH+AsDb8ew8Ec8HMOmpyAEHfYyRAdcbeu +bh4XtG4ccTpqFtZS+1YEqUowq8lwIYM6gC9m4Mqj+/ZfRl9Ul5CSLpk9GpaWqyAt +HI5JHoCDWI9RPuQ55yajpzPvcbCidV/nQxiocs60gQKBgQD67u9cMau9TZzu/aML +A2nk3DoYzPIoZ7cD5LdxV6EWZJ507+MIcbqdBAuarrkcfrToiN4sq/502ExHCclO +lorMj8cO+AetLMzRfIVTsVrBHjvoVTNOYffF+d8qZ7sYIR2MsuT4XZbJbVb3fD+5 +52PTNOJF07lup7rr7dudVZXJqwKBgQDgfFwt/JGSClOeGhBTm1avAinIkEzyzjr/ +HYHlzChFZeqSm5P3RuI9sGvMQs9oJQcze7tEAndRXhVXZ3jJfiFUKb+1exNjcWyV +yFAACi5n1Yl9ZJRieQsXxhzLE7XiKvknipvwyA+Wf5SY2AR0uvAQSZFyBP3aF8I6 +0dntXxjcCQKBgBQmwgQmXQNby0GKDuDgik19vhClzMCf65udb3njrqUMuYjshs/z +yie33nKym10Fc+PEsgrmWQ3rHN0LlSYBgu4AkdzK4Frw4RXlqRNah85AblEvHmqq +BWrZsSlHoUfDyQq2hxrG8UgFxtkjGj8ErQiWE6HF6ftP7vvpYxyUde33AoGBAKCW +9k6DhxPFRWot1Q4qawmJb6Cl8hYkiDnmvv1IXmq+7N7yYxibYc+lvIgEJ2GmU491 +7VJKvVa9CmFbiSIDlA4kS5ulLwqNopNIGEre/bGUJeZJJImJc/EO7ZwtPolMGq85 +zjKiu9v66q+ooQ9sh73vExhRD9SL+Iuhgdf4ls0ZAoGBAMwcYLCPVI01EDpKR6RA +tVcHQ2SDeG0i5C93DtNLFoe/pWLtB9VKqHpNVduP4u9m6g5tGIQUd5tb167Fv37c +JMXg5tH3fzPSNyrwxVYgPghByCnU+2EuSoW8Le907LqEzSK8bGWWAIhEEdGPpCXd +9U3aIgpCmgCxfMwT/pGQNcIC +-----END PRIVATE KEY----- diff --git a/contrib/tlsauth/testdata/client2.crt b/contrib/tlsauth/testdata/client2.crt new file mode 100644 index 0000000..e50615b --- /dev/null +++ b/contrib/tlsauth/testdata/client2.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrDCCAZQCAQAwDQYJKoZIhvcNAQELBQAwHDEaMBgGA1UEAwwRYW5vdGhlciB0 +ZXN0IHVzZXIwHhcNMjMwMTIwMDIwOTE3WhcNMzMwMTE5MDcwMDAwWjAcMRowGAYD +VQQDDBFhbm90aGVyIHRlc3QgdXNlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALoYWzKowSNs4sI94rbqHQQB03vU6i0Yv6A4FMS8qtiOKIUjoEvPKD3V +QyTWv8caYJlQX8C2BcOpCQ6ZXdycTGAA2Mo0ssRnGCmhIHmzKOvpFYAcLgTTiTK/ +nuOU+pOR29V2gwQtcTU0zxs1C9YOn946TErwkYIUf7fIDUWjSCuLgFa4H2qtJ5D6 +dklEWe73Fdd1BSywrcI7Y5jCL+quPLpVxYQv3OS6/yk3XV91O+CXCeTskE8pFR+d +m8Qhf2ZVov/cI1CZ9+DerYlX/9vlCGyb6EdvV+2DjPId+INh/ac8RSOK8RwL3U8j +qPVcsK/sKRapBECT++C5KJYJZ/rqkMkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA +DyUuK7A+jxgHHezZ+7RzYwWkBUL/tRtXfwax114MYVJPN25rJ0T0lDncgXjE6qg1 +cSK4ERJ+qi6Bj+ACXh/iaLNGnidWcGg6HHCCJCiTjco2SIxq0ytEe8znN6uhVj+d +pSdt8ICDhimr+yMZUYs0q7rMzPFUJSUPGi50mo/BubSuPzImaBLbS5TJ/sF/EAbC +Q+zRo5qjlehZatlXUYp+Zh5pHTvLDtmkHU5dQibbf8cYncuCCtjpbsjLXGNsi29c +OWqk+BKOEjGfCgUDjxzt5TXREA5MsCIVnrnU2lkhUUgfDVIoLK32p21NMKlXiIH0 +yGVD41bZ6d+ZDO5LU0LjzA== +-----END CERTIFICATE----- diff --git a/contrib/tlsauth/testdata/client2.key b/contrib/tlsauth/testdata/client2.key new file mode 100644 index 0000000..d43cd04 --- /dev/null +++ b/contrib/tlsauth/testdata/client2.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6GFsyqMEjbOLC +PeK26h0EAdN71OotGL+gOBTEvKrYjiiFI6BLzyg91UMk1r/HGmCZUF/AtgXDqQkO +mV3cnExgANjKNLLEZxgpoSB5syjr6RWAHC4E04kyv57jlPqTkdvVdoMELXE1NM8b +NQvWDp/eOkxK8JGCFH+3yA1Fo0gri4BWuB9qrSeQ+nZJRFnu9xXXdQUssK3CO2OY +wi/qrjy6VcWEL9zkuv8pN11fdTvglwnk7JBPKRUfnZvEIX9mVaL/3CNQmffg3q2J +V//b5Qhsm+hHb1ftg4zyHfiDYf2nPEUjivEcC91PI6j1XLCv7CkWqQRAk/vguSiW +CWf66pDJAgMBAAECggEARzlpNjNmcGONSmCbM/zYjB8SzTNJSWdOeEjekgnPrcCC ++6oOANXRhhDoeOIEVnTfAe7EJyLDhAZfJApI5VWg2aGZV5Lh1M/MbKpxnoKWp+v2 +waiHaGt5+EVkz5/GY9KQe9u2+1NVH9MNbVFZLV09jLVtW8VFO68SzskvYzbCOX+s +0xzaKIvgZGUxgYIR8+W422hugKxSi31RTG/Os7MViKH0eEavFZDzc4qbesOxFWKA +ayfSIfeJVPZnDG5SQTT63QxNLHz9b8oZlmXagfxgxvHVCJc9wgOayQ3fM01a/QLP +XMLmDOhUn+9SB2c09Ky6tGasQpCLdzXjlmqWcemobQKBgQDb4DjBqT2KhhFkuX7G +3c+9ak4XhGkvDXZ90McUrZ5gH8T6t49tXokrzvWZhf36MsLhbCK+x4IEkPgGhTOD +ZkdIDRU/Fn/mnRaLZuoO4Y39lXm/x1evHI8mP5ker9pJwlKeUNp8qH+UVeG23UCO +j0fMaVi+3R1ILZugOXOyL9xBUwKBgQDYq1kcZts6NC2QFwHToZgN1rifHsYpK6v5 +9g93V/JyAPgBbihGfjkp4pwpD0qI7/VZsw14zNKkSSAeFLaekWtc5b31ejmlW5ZQ +PZqVdhcftWVgO0zmtBkqZzDsY7Itdzgsd1y/CiVCU6daoMcPyc/GY//KAR6aOkqv +w6zMOzhV8wKBgBmm+Tgu5I0qwxC7S30sF7aDloTL3/GrYm2fU/qnnticHEEb9VHP +O7WuuZyls1HjZjUihpM3d4XM3AL2u2HTJvHTBO2NVHK1VRICecAutIAnVkL3oNU/ +Qbw2o0ifP5pnX3g0+qich/XoZyMMgSGgucGxcLcj2Oy41XVF/qeFSe8dAoGAB9az +I57pAYMPvu3GKCTpfl6tUTxoyOaGk0V+q7+nys4UKuEUXfCFGunS4n1mIewkgTlE +HIG0gTMQEWaIcNYr/zFknPPuD/hvSLnh1NRv47rJTyD2GEadvnX7RCIbOR/eDWXI +GjVAwdSK8nFsojqX5MKLZ4CA3e2L9C3tG3ptAFcCgYEAgcAXUyCy8bs6pqwRsVbk +g/vpHrOpCyWbpKTu9ErXsc6Lf6wGP/yzAHuUSznPSEs7JPCqCk9SGumS20OTKrmp +yvFdaOrdVh51NY+ag8I5FpcWifc6YD14qenxfRapqjxi2r9WnptVfQvTNotsa44S +V7ueLsLSkYsIfAq9bYQsg5U= +-----END PRIVATE KEY----- diff --git a/contrib/tlsauth/testdata/server.crt b/contrib/tlsauth/testdata/server.crt new file mode 100644 index 0000000..4e3dc74 --- /dev/null +++ b/contrib/tlsauth/testdata/server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7jCCAdYCAQcwDQYJKoZIhvcNAQELBQAwPTESMBAGA1UEAwwJbG9jYWxob3N0 +MQswCQYDVQQGEwJVUzEaMBgGA1UEBwwRU2FuIEZyYW5jaXNjbywgQ0EwHhcNMjMw +MTExMjAwMDU5WhcNMjUwNDE1MjAwMDU5WjA9MRIwEAYDVQQDDAlsb2NhbGhvc3Qx +CzAJBgNVBAYTAlVTMRowGAYDVQQHDBFTYW4gRnJhbmNpc2NvLCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALlaPa1AxDQnMo0qQxY5/Bf7MNf1x6tN +xjkpMnQnPM+cHmmlkEhI1zwLk/LrLxwq7+OOxMTPrJglrAiDAp1uCZHjKcTMFnwO +9M5vf8LjtYBjZd8+OSHyYV37gxw7h9/Wsxl+1Yw40QaJKM9auj2xOyaDj5Ou9+yp +CfbGSpVUTnqReOVFg2QSNwEviOZu1SvAouPyO98WKoXjn7K5mxE545e4mgF1EMht +jB5kH6kXqZSUszlGA1MkX3AlDsYJIcYnDwelNvw6XTPpkT2wNehxPyD0iP4rs+W4 +5hgV8wYokpgrM3xxe0c4mop5bzrp2Hyz3WxnF7KwtJgHW/6YxhG73skCAwEAATAN +BgkqhkiG9w0BAQsFAAOCAQEAfI+UE/3d0Fb8BZ2gtv1kUh8yx75LUbpg1aOEsZdP +Rji+GkL5xiFDsm7BwqTKziAjDtjL2qtGcJJ835shsGiUSK6qJuf9C944utUvCoFm +b4aUZ8fTmN7PkwRS61nIcHaS1zkiFzUdvbquV3QWSnl9kC+yDLHT0Z535tcvCMVM +bO7JMj1sxml4Y9B/hfY7zAZJt1giSNH1iDeX2pTpmPPI40UsRn98cC8HZ0d8wFrv +yc3hKkz8E+WTgZUf7jFk/KX/T5uwu+Y85emwfbb82KIR3oqhkJIfOfpqop2duZXB +hMuO1QWEBkZ/hpfrAsN/foz8v46P9qgW8gfOfzhyBcqLvA== +-----END CERTIFICATE----- diff --git a/contrib/tlsauth/testdata/server.key b/contrib/tlsauth/testdata/server.key new file mode 100644 index 0000000..44ce348 --- /dev/null +++ b/contrib/tlsauth/testdata/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAuVo9rUDENCcyjSpDFjn8F/sw1/XHq03GOSkydCc8z5weaaWQ +SEjXPAuT8usvHCrv447ExM+smCWsCIMCnW4JkeMpxMwWfA70zm9/wuO1gGNl3z45 +IfJhXfuDHDuH39azGX7VjDjRBokoz1q6PbE7JoOPk6737KkJ9sZKlVROepF45UWD +ZBI3AS+I5m7VK8Ci4/I73xYqheOfsrmbETnjl7iaAXUQyG2MHmQfqReplJSzOUYD +UyRfcCUOxgkhxicPB6U2/DpdM+mRPbA16HE/IPSI/iuz5bjmGBXzBiiSmCszfHF7 +RziainlvOunYfLPdbGcXsrC0mAdb/pjGEbveyQIDAQABAoIBAQC36ylkLu4Bahup +I5RqC6NwEFpJEKLOAmB8+7oKs5yNzTYIUra2Y0DfXgWyd1fJtXlP7aymNgPm/QqV +b5o6qKNqVWRu2Kw+8YBNDypRMi45dWfyewWp/55J6XYRn6iVna8dz1MKzp3qxFLw +XfCLor802jqvqmBsPteaPOxo/LzatKhXp/mcO/hsxeMr1iSUVHTrQEIU/aIkmAqT +/eXp/zVZk7O9Tx8wwCijB3v7j3zTEkcKSwFlAp0w01XeqllmqA5P9rW3vVGXJVIM +t6t9C8XcJWPIOURz3JWZJpUBSZsyNe2N/wbCgkQV81A0s+4praKzgDbjE+njb0C/ +1CClbHV5AoGBAO/mnOzHe7ZJyYfuiu6ZR2REBY61n2J6DkL1stkN5xd+Op25afHT +jLBjU98hM/AMtP1aHWFQpdEe0uyqRjV6PbpNE8j/m9AVfjZxzwR4ITW2xqUhXOSz +89o832RO54TTr19YGnIhdU8dDQmYOcKmCSuw6KwCfHwBzkFuDFZGk/4/AoGBAMXK +gzNyX3tN9Ug5AUo/Az4jQRSoyLjfnce0a0TF4jxEacUBx2COq3zaV/VADEFBla1t +5roOAUyJ3V6fXtZnoqwZPYh6iGP8p7Tj6vyXI4SDktV0uAV57qSdajqxTrA7yoXr +zrbxv3U/3vXr3JTsP42U5zp1m5n1VfVqCXBkynD3AoGBAOvs7JjDWXuctzASPNmH +LjmB18FQBk3vYQUi4l8pmAF3pyejx3gGJw70r+/4lD5YEMozjD8+88Njv+T1U5SW +Agysbm+2SMJr0LK0W/W2Olq7xEFzPQrBmmgeg0b/fhoXoBlw6JkjJF3IYSD1bqBp +bw1jrn4y979weynHkyRpxnM7AoGBALGSzRPlPR/gr7P1qdjUlb61u/omRn7kFC11 +J1EJL8HX0fXTUQK5U/C1vn4q0FXN4elgX+LuK/BhXeNTxbtMM9m6l2nuSIEsFgzr +Cs9XicWwsqT9MzGHdN9JjFPBV9oU9BAj0uSgSbmkbDHxXYo+SBh+dNIhQF+KyW+Z +kXvcoXulAoGAA2hnEA17nJ7Vj1DZ4CoRblgjZFAMB64slcSesaorp3WWehvaXO8u +jbvWuvj58DgvTLiv8xPIn4Zsjd0a77ysifvUcmxSRa/k9UIle/lwjmXGjQ1GSMEI +FB5ZTqjLZwS9Y5BDxlPcYF7vqE9fNpcxmcfHGmSF5YAHvFOfGH6B63M= +-----END RSA PRIVATE KEY----- -- cgit v1.2.3