diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go
index 8c83ae42c5..91cbd72f91 100644
--- a/integrations/api_keys_test.go
+++ b/integrations/api_keys_test.go
@@ -7,8 +7,11 @@ package integrations
 import (
 	"fmt"
 	"net/http"
+	"net/url"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
+
 	"code.gitea.io/gitea/models"
 	api "code.gitea.io/sdk/gitea"
 )
@@ -90,3 +93,102 @@ func TestCreateReadWriteDeployKey(t *testing.T) {
 		Mode:    models.AccessModeWrite,
 	})
 }
+
+func TestCreateUserKey(t *testing.T) {
+	prepareTestEnv(t)
+	user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
+
+	session := loginUser(t, "user1")
+	token := url.QueryEscape(getTokenForLoggedInUser(t, session))
+	keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token)
+	keyType := "ssh-rsa"
+	keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCyTiPTeHJl6Gs5D1FyHT0qTWpVkAy9+LIKjctQXklrePTvUNVrSpt4r2exFYXNMPeA8V0zCrc3Kzs1SZw3jWkG3i53te9onCp85DqyatxOD2pyZ30/gPn1ZUg40WowlFM8gsUFMZqaH7ax6d8nsBKW7N/cRyqesiOQEV9up3tnKjIB8XMTVvC5X4rBWgywz7AFxSv8mmaTHnUgVW4LgMPwnTWo0pxtiIWbeMLyrEE4hIM74gSwp6CRQYo6xnG3fn4yWkcK2X2mT9adQ241IDdwpENJHcry/T6AJ8dNXduEZ67egnk+rVlQ2HM4LpymAv9DAAFFeaQK0hT+3aMDoumV"
+	rawKeyBody := api.CreateKeyOption{
+		Title: "test-key",
+		Key:   keyType + " " + keyContent,
+	}
+	req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody)
+	resp := session.MakeRequest(t, req, http.StatusCreated)
+
+	var newPublicKey api.PublicKey
+	DecodeJSON(t, resp, &newPublicKey)
+	models.AssertExistsAndLoadBean(t, &models.PublicKey{
+		ID:      newPublicKey.ID,
+		OwnerID: user.ID,
+		Name:    rawKeyBody.Title,
+		Content: rawKeyBody.Key,
+		Mode:    models.AccessModeWrite,
+	})
+
+	// Search by fingerprint
+	fingerprintURL := fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token, newPublicKey.Fingerprint)
+
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	var fingerprintPublicKeys []api.PublicKey
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+	assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+	assert.Equal(t, user.ID, fingerprintPublicKeys[0].Owner.ID)
+
+	fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", user.Name, token, newPublicKey.Fingerprint)
+
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+	assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+	assert.Equal(t, user.ID, fingerprintPublicKeys[0].Owner.ID)
+
+	// Fail search by fingerprint
+	fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%sA", token, newPublicKey.Fingerprint)
+
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Len(t, fingerprintPublicKeys, 0)
+
+	// Fail searching for wrong users key
+	fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", "user2", token, newPublicKey.Fingerprint)
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Len(t, fingerprintPublicKeys, 0)
+
+	// Now login as user 2
+	session2 := loginUser(t, "user2")
+	token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2))
+
+	// Should find key even though not ours, but we shouldn't know whose it is
+	fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint)
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+	assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+	assert.Nil(t, fingerprintPublicKeys[0].Owner)
+
+	// Should find key even though not ours, but we shouldn't know whose it is
+	fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", user.Name, token2, newPublicKey.Fingerprint)
+
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+	assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+	assert.Nil(t, fingerprintPublicKeys[0].Owner)
+
+	// Fail when searching for key if it is not ours
+	fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", "user2", token2, newPublicKey.Fingerprint)
+	req = NewRequest(t, "GET", fingerprintURL)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	DecodeJSON(t, resp, &fingerprintPublicKeys)
+	assert.Len(t, fingerprintPublicKeys, 0)
+}
diff --git a/models/ssh_key.go b/models/ssh_key.go
index 2592209b4d..0368ffad33 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -24,6 +24,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 
 	"github.com/Unknwon/com"
+	"github.com/go-xorm/builder"
 	"github.com/go-xorm/xorm"
 	"golang.org/x/crypto/ssh"
 )
@@ -465,6 +466,19 @@ func SearchPublicKeyByContent(content string) (*PublicKey, error) {
 	return key, nil
 }
 
+// SearchPublicKey returns a list of public keys matching the provided arguments.
+func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
+	keys := make([]*PublicKey, 0, 5)
+	cond := builder.NewCond()
+	if uid != 0 {
+		cond = cond.And(builder.Eq{"owner_id": uid})
+	}
+	if fingerprint != "" {
+		cond = cond.And(builder.Eq{"fingerprint": fingerprint})
+	}
+	return keys, x.Where(cond).Find(&keys)
+}
+
 // ListPublicKeys returns a list of public keys belongs to given user.
 func ListPublicKeys(uid int64) ([]*PublicKey, error) {
 	keys := make([]*PublicKey, 0, 5)
@@ -833,3 +847,19 @@ func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
 		Where("repo_id = ?", repoID).
 		Find(&keys)
 }
+
+// SearchDeployKeys returns a list of deploy keys matching the provided arguments.
+func SearchDeployKeys(repoID int64, keyID int64, fingerprint string) ([]*DeployKey, error) {
+	keys := make([]*DeployKey, 0, 5)
+	cond := builder.NewCond()
+	if repoID != 0 {
+		cond = cond.And(builder.Eq{"repo_id": repoID})
+	}
+	if keyID != 0 {
+		cond = cond.And(builder.Eq{"key_id": keyID})
+	}
+	if fingerprint != "" {
+		cond = cond.And(builder.Eq{"fingerprint": fingerprint})
+	}
+	return keys, x.Where(cond).Find(&keys)
+}
diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go
index 19b966971a..1bfeae34bf 100644
--- a/routers/api/v1/convert/convert.go
+++ b/routers/api/v1/convert/convert.go
@@ -167,12 +167,14 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
 // ToDeployKey convert models.DeployKey to api.DeployKey
 func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
 	return &api.DeployKey{
-		ID:       key.ID,
-		Key:      key.Content,
-		URL:      apiLink + com.ToStr(key.ID),
-		Title:    key.Name,
-		Created:  key.CreatedUnix.AsTime(),
-		ReadOnly: true, // All deploy keys are read-only.
+		ID:          key.ID,
+		KeyID:       key.KeyID,
+		Key:         key.Content,
+		Fingerprint: key.Fingerprint,
+		URL:         apiLink + com.ToStr(key.ID),
+		Title:       key.Name,
+		Created:     key.CreatedUnix.AsTime(),
+		ReadOnly:    key.Mode == models.AccessModeRead, // All deploy keys are read-only.
 	}
 }
 
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 89a550cfd3..2caca887aa 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -15,6 +15,21 @@ import (
 	api "code.gitea.io/sdk/gitea"
 )
 
+// appendPrivateInformation appends the owner and key type information to api.PublicKey
+func appendPrivateInformation(apiKey *api.DeployKey, key *models.DeployKey, repository *models.Repository) (*api.DeployKey, error) {
+	apiKey.ReadOnly = key.Mode == models.AccessModeRead
+	if repository.ID == key.RepoID {
+		apiKey.Repository = repository.APIFormat(key.Mode)
+	} else {
+		repo, err := models.GetRepositoryByID(key.RepoID)
+		if err != nil {
+			return apiKey, err
+		}
+		apiKey.Repository = repo.APIFormat(key.Mode)
+	}
+	return apiKey, nil
+}
+
 func composeDeployKeysAPILink(repoPath string) string {
 	return setting.AppURL + "api/v1/repos/" + repoPath + "/keys/"
 }
@@ -37,10 +52,28 @@ func ListDeployKeys(ctx *context.APIContext) {
 	//   description: name of the repo
 	//   type: string
 	//   required: true
+	// - name: key_id
+	//   in: query
+	//   description: the key_id to search for
+	//   type: integer
+	// - name: fingerprint
+	//   in: query
+	//   description: fingerprint of the key
+	//   type: string
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/DeployKeyList"
-	keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
+	var keys []*models.DeployKey
+	var err error
+
+	fingerprint := ctx.Query("fingerprint")
+	keyID := ctx.QueryInt64("key_id")
+	if fingerprint != "" || keyID != 0 {
+		keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint)
+	} else {
+		keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID)
+	}
+
 	if err != nil {
 		ctx.Error(500, "ListDeployKeys", err)
 		return
@@ -54,6 +87,9 @@ func ListDeployKeys(ctx *context.APIContext) {
 			return
 		}
 		apiKeys[i] = convert.ToDeployKey(apiLink, keys[i])
+		if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == keys[i].RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
+			apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], ctx.Repo.Repository)
+		}
 	}
 
 	ctx.JSON(200, &apiKeys)
@@ -102,7 +138,11 @@ func GetDeployKey(ctx *context.APIContext) {
 	}
 
 	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
-	ctx.JSON(200, convert.ToDeployKey(apiLink, key))
+	apiKey := convert.ToDeployKey(apiLink, key)
+	if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
+		apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
+	}
+	ctx.JSON(200, apiKey)
 }
 
 // HandleCheckKeyStringError handle check key error
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index e5d1b08f0d..d8ab752b2b 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -14,6 +14,29 @@ import (
 	"code.gitea.io/gitea/routers/api/v1/repo"
 )
 
+// appendPrivateInformation appends the owner and key type information to api.PublicKey
+func appendPrivateInformation(apiKey *api.PublicKey, key *models.PublicKey, defaultUser *models.User) (*api.PublicKey, error) {
+	if key.Type == models.KeyTypeDeploy {
+		apiKey.KeyType = "deploy"
+	} else if key.Type == models.KeyTypeUser {
+		apiKey.KeyType = "user"
+
+		if defaultUser.ID == key.OwnerID {
+			apiKey.Owner = defaultUser.APIFormat()
+		} else {
+			user, err := models.GetUserByID(key.OwnerID)
+			if err != nil {
+				return apiKey, err
+			}
+			apiKey.Owner = user.APIFormat()
+		}
+	} else {
+		apiKey.KeyType = "unknown"
+	}
+	apiKey.ReadOnly = key.Mode == models.AccessModeRead
+	return apiKey, nil
+}
+
 // GetUserByParamsName get user by name
 func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
 	user, err := models.GetUserByName(ctx.Params(name))
@@ -37,8 +60,27 @@ func composePublicKeysAPILink() string {
 	return setting.AppURL + "api/v1/user/keys/"
 }
 
-func listPublicKeys(ctx *context.APIContext, uid int64) {
-	keys, err := models.ListPublicKeys(uid)
+func listPublicKeys(ctx *context.APIContext, user *models.User) {
+	var keys []*models.PublicKey
+	var err error
+
+	fingerprint := ctx.Query("fingerprint")
+	username := ctx.Params("username")
+
+	if fingerprint != "" {
+		// Querying not just listing
+		if username != "" {
+			// Restrict to provided uid
+			keys, err = models.SearchPublicKey(user.ID, fingerprint)
+		} else {
+			// Unrestricted
+			keys, err = models.SearchPublicKey(0, fingerprint)
+		}
+	} else {
+		// Use ListPublicKeys
+		keys, err = models.ListPublicKeys(user.ID)
+	}
+
 	if err != nil {
 		ctx.Error(500, "ListPublicKeys", err)
 		return
@@ -48,6 +90,9 @@ func listPublicKeys(ctx *context.APIContext, uid int64) {
 	apiKeys := make([]*api.PublicKey, len(keys))
 	for i := range keys {
 		apiKeys[i] = convert.ToPublicKey(apiLink, keys[i])
+		if ctx.User.IsAdmin || ctx.User.ID == keys[i].OwnerID {
+			apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], user)
+		}
 	}
 
 	ctx.JSON(200, &apiKeys)
@@ -58,12 +103,17 @@ func ListMyPublicKeys(ctx *context.APIContext) {
 	// swagger:operation GET /user/keys user userCurrentListKeys
 	// ---
 	// summary: List the authenticated user's public keys
+	// parameters:
+	// - name: fingerprint
+	//   in: query
+	//   description: fingerprint of the key
+	//   type: string
 	// produces:
 	// - application/json
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/PublicKeyList"
-	listPublicKeys(ctx, ctx.User.ID)
+	listPublicKeys(ctx, ctx.User)
 }
 
 // ListPublicKeys list the given user's public keys
@@ -79,6 +129,10 @@ func ListPublicKeys(ctx *context.APIContext) {
 	//   description: username of user
 	//   type: string
 	//   required: true
+	// - name: fingerprint
+	//   in: query
+	//   description: fingerprint of the key
+	//   type: string
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/PublicKeyList"
@@ -86,7 +140,7 @@ func ListPublicKeys(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	listPublicKeys(ctx, user.ID)
+	listPublicKeys(ctx, user)
 }
 
 // GetPublicKey get a public key
@@ -119,7 +173,11 @@ func GetPublicKey(ctx *context.APIContext) {
 	}
 
 	apiLink := composePublicKeysAPILink()
-	ctx.JSON(200, convert.ToPublicKey(apiLink, key))
+	apiKey := convert.ToPublicKey(apiLink, key)
+	if ctx.User.IsAdmin || ctx.User.ID == key.OwnerID {
+		apiKey, _ = appendPrivateInformation(apiKey, key, ctx.User)
+	}
+	ctx.JSON(200, apiKey)
 }
 
 // CreateUserPublicKey creates new public key to given user by ID.
@@ -136,7 +194,11 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid
 		return
 	}
 	apiLink := composePublicKeysAPILink()
-	ctx.JSON(201, convert.ToPublicKey(apiLink, key))
+	apiKey := convert.ToPublicKey(apiLink, key)
+	if ctx.User.IsAdmin || ctx.User.ID == key.OwnerID {
+		apiKey, _ = appendPrivateInformation(apiKey, key, ctx.User)
+	}
+	ctx.JSON(201, apiKey)
 }
 
 // CreatePublicKey create one public key for me
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index efba90b18b..56a169c295 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -2682,6 +2682,18 @@
             "name": "repo",
             "in": "path",
             "required": true
+          },
+          {
+            "type": "integer",
+            "description": "the key_id to search for",
+            "name": "key_id",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "description": "fingerprint of the key",
+            "name": "fingerprint",
+            "in": "query"
           }
         ],
         "responses": {
@@ -4976,6 +4988,14 @@
         ],
         "summary": "List the authenticated user's public keys",
         "operationId": "userCurrentListKeys",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "fingerprint of the key",
+            "name": "fingerprint",
+            "in": "query"
+          }
+        ],
         "responses": {
           "200": {
             "$ref": "#/responses/PublicKeyList"
@@ -5540,6 +5560,12 @@
             "name": "username",
             "in": "path",
             "required": true
+          },
+          {
+            "type": "string",
+            "description": "fingerprint of the key",
+            "name": "fingerprint",
+            "in": "query"
           }
         ],
         "responses": {