diff --git a/design/design.go b/design/design.go
index eafcfefa6ba78fd0b8be5cf2a3583c822ae377bf..cb8477f5eb610cabf6a2496b4f4025f73d08a509 100644
--- a/design/design.go
+++ b/design/design.go
@@ -58,6 +58,18 @@ var _ = Service("signer", func() {
 		})
 	})
 
+	Method("JwkPublicKey", func() {
+		Description("JwkPublicKey returns public key by name and namespace.")
+		Payload(JwkPublicKeyRequest)
+		Result(Any, "Public key encoded as JSON Web Key.")
+		HTTP(func() {
+			GET("/v1/jwk/{namespace}/{key}")
+			Response(StatusOK)
+			Response(StatusNotFound)
+			Response(StatusInternalServerError)
+		})
+	})
+
 	Method("CredentialProof", func() {
 		Description("CredentialProof adds a proof to a given Verifiable Credential.")
 		Payload(CredentialProofRequest)
diff --git a/design/types.go b/design/types.go
index bbcc86a7dbe8c4d04e1462577c1191d6fe1e8c4a..dd8ee49915247d8ef34c4cba261dac1a71d3c386 100644
--- a/design/types.go
+++ b/design/types.go
@@ -159,6 +159,16 @@ var DIDVerificationMethod = Type("DIDVerificationMethod", func() {
 	Required("id", "type", "controller", "publicKeyJwk")
 })
 
+var JwkPublicKeyRequest = Type("JwkPublicKeyRequest", func() {
+	Field(1, "namespace", String, "Key namespace.", func() {
+		Example("transit")
+	})
+	Field(2, "key", String, "Key name.", func() {
+		Example("my-ecdsa-key1")
+	})
+	Required("namespace", "key")
+})
+
 var SignRequest = Type("SignRequest", func() {
 	Field(1, "namespace", String, "Key namespace to be used for signing.")
 	Field(2, "key", String, "Key to be used for signing.")
diff --git a/internal/service/signer/service.go b/internal/service/signer/service.go
index 685ecd112e4d870f4f58e8601753780d316307c4..889dcfb14827cf31775ed84425bba33e6f95d32c 100644
--- a/internal/service/signer/service.go
+++ b/internal/service/signer/service.go
@@ -195,6 +195,33 @@ func (s *Service) VerificationMethods(ctx context.Context, req *signer.Verificat
 	return res, nil
 }
 
+// JwkPublicKey returns public key by name and namespace.
+func (s *Service) JwkPublicKey(ctx context.Context, req *signer.JwkPublicKeyRequest) (any, error) {
+	logger := s.logger.With(
+		zap.String("operation", "jwkPublicKey"),
+		zap.String("namespace", req.Namespace),
+		zap.String("key", req.Key),
+	)
+
+	key, err := s.vault.Key(ctx, req.Namespace, req.Key)
+	if err != nil {
+		logger.Error("error getting key", zap.Error(err))
+		return nil, err
+	}
+
+	pubKey, err := s.jwkFromKey(key)
+	if err != nil {
+		logger.Error("error converting public key to jwk",
+			zap.String("key", key.Name),
+			zap.String("keyType", key.Type),
+			zap.Error(err),
+		)
+		return nil, fmt.Errorf("error converting public key to jwk: %v", err)
+	}
+
+	return pubKey, nil
+}
+
 // CredentialProof adds a proof to a given Verifiable Credential.
 func (s *Service) CredentialProof(ctx context.Context, req *signer.CredentialProofRequest) (interface{}, error) {
 	logger := s.logger.With(
diff --git a/internal/service/signer/service_test.go b/internal/service/signer/service_test.go
index 70c919ad404ace0ed2545cf1811e80539e1aae7a..e1b0c4b498c8aca27dce7489bba1780dc8a203b9 100644
--- a/internal/service/signer/service_test.go
+++ b/internal/service/signer/service_test.go
@@ -7,7 +7,9 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"net"
 	"net/http"
+	"os"
 	"testing"
 	"time"
 
@@ -26,10 +28,26 @@ import (
 
 var docLoader *ld.CachingDocumentLoader
 
-func init() {
+func TestMain(m *testing.M) {
+	c := &http.Client{
+		Transport: &http.Transport{
+			Proxy: http.ProxyFromEnvironment,
+			DialContext: (&net.Dialer{
+				Timeout: 5 * time.Second,
+			}).DialContext,
+			MaxIdleConns:        1,
+			MaxIdleConnsPerHost: 1,
+			TLSHandshakeTimeout: 5 * time.Second,
+			IdleConnTimeout:     60 * time.Second,
+		},
+		Timeout: 10 * time.Second,
+	}
+
 	if docLoader == nil {
-		docLoader = ld.NewCachingDocumentLoader(ld.NewDefaultDocumentLoader(http.DefaultClient))
+		docLoader = ld.NewCachingDocumentLoader(ld.NewDefaultDocumentLoader(c))
 	}
+
+	os.Exit(m.Run())
 }
 
 func TestService_Namespaces(t *testing.T) {
@@ -262,6 +280,52 @@ func TestService_VerificationMethods(t *testing.T) {
 	})
 }
 
+func TestService_JwkPublicKey(t *testing.T) {
+	t.Run("signer returns error when getting key", func(t *testing.T) {
+		vaultError := &signerfakes.FakeVault{
+			KeyStub: func(ctx context.Context, namespace, key string) (*signer.VaultKey, error) {
+				return nil, errors.New(errors.NotFound, "key not found")
+			},
+		}
+
+		svc := signer.New(vaultError, []string{}, docLoader, zap.NewNop())
+		result, err := svc.JwkPublicKey(
+			context.Background(),
+			&goasigner.JwkPublicKeyRequest{Namespace: "transit", Key: "key1"},
+		)
+		assert.Nil(t, result)
+		assert.Error(t, err)
+		e, ok := err.(*errors.Error)
+		assert.True(t, ok)
+		assert.Equal(t, errors.NotFound, e.Kind)
+	})
+
+	t.Run("signer returns ecdsa-p256 key successfully", func(t *testing.T) {
+		signerOK := &signerfakes.FakeVault{
+			KeyStub: func(ctx context.Context, namespace, key string) (*signer.VaultKey, error) {
+				return &signer.VaultKey{
+					Name:      "key1",
+					Type:      "ecdsa-p256",
+					PublicKey: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERTx/2cyYcGVSIRP/826S32BiZxSg\nnzyXgRYmKP8N2l26ec/MwCdsHIEyraX1ZYqwMUT4wO9fqFiGsRKyMBpPnQ==\n-----END PUBLIC KEY-----\n",
+				}, nil
+			},
+		}
+
+		svc := signer.New(signerOK, []string{"ecdsa-p256"}, docLoader, zap.NewNop())
+		result, err := svc.JwkPublicKey(
+			context.Background(),
+			&goasigner.JwkPublicKeyRequest{Namespace: "transit", Key: "key1"},
+		)
+		assert.NotNil(t, result)
+		assert.NoError(t, err)
+
+		pub, ok := result.(*jose.JSONWebKey)
+		assert.True(t, ok)
+		assert.NotNil(t, pub)
+		assert.IsType(t, (*ecdsa.PublicKey)(nil), pub.Key)
+	})
+}
+
 func TestService_CredentialProof(t *testing.T) {
 	tests := []struct {
 		name          string
@@ -428,7 +492,7 @@ func TestService_CredentialProof(t *testing.T) {
 			})
 			if err != nil {
 				assert.Nil(t, res)
-				require.NotEmpty(t, test.errtext, "error is not expected, but got: %v ")
+				require.NotEmpty(t, test.errtext, "error is not expected, but got: %v ", err)
 				assert.Contains(t, err.Error(), test.errtext)
 				if e, ok := err.(*errors.Error); ok {
 					assert.Equal(t, test.errkind, e.Kind)
@@ -451,7 +515,7 @@ func TestService_CredentialProof(t *testing.T) {
 			}
 		})
 
-		time.Sleep(500 * time.Millisecond)
+		time.Sleep(1 * time.Second)
 	}
 }
 
@@ -652,7 +716,7 @@ func TestService_PresentationProof(t *testing.T) {
 			assert.NotEmpty(t, vp.Proofs[0]["jws"])
 		})
 
-		time.Sleep(500 * time.Millisecond)
+		time.Sleep(1 * time.Second)
 	}
 }
 
@@ -815,6 +879,8 @@ func TestService_CreateCredential(t *testing.T) {
 				assert.Equal(t, test.wantedCredentialSubject, vc.Subject)
 			}
 		})
+
+		time.Sleep(1 * time.Second)
 	}
 }