diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go
index 9116d45840..13de492f92 100644
--- a/cli/command/image/pull.go
+++ b/cli/command/image/pull.go
@@ -55,16 +55,6 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag)
 	}
 
-	var tag string
-	switch x := distributionRef.(type) {
-	case reference.Canonical:
-		tag = x.Digest().String()
-	case reference.NamedTagged:
-		tag = x.Tag()
-	}
-
-	registryRef := registry.ParseReference(tag)
-
 	// Resolve the Repository name from fqn to RepositoryInfo
 	repoInfo, err := registry.ParseRepositoryInfo(distributionRef)
 	if err != nil {
@@ -76,9 +66,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
 	requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "pull")
 
-	if command.IsTrusted() && !registryRef.HasDigest() {
-		// Check if tag is digest
-		err = trustedPull(ctx, dockerCli, repoInfo, registryRef, authConfig, requestPrivilege)
+	// Check if reference has a digest
+	_, isCanonical := distributionRef.(reference.Canonical)
+	if command.IsTrusted() && !isCanonical {
+		err = trustedPull(ctx, dockerCli, repoInfo, distributionRef, authConfig, requestPrivilege)
 	} else {
 		err = imagePullPrivileged(ctx, dockerCli, authConfig, distributionRef.String(), requestPrivilege, opts.all)
 	}
diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go
index d1106b532e..f32c301959 100644
--- a/cli/command/image/trust.go
+++ b/cli/command/image/trust.go
@@ -6,49 +6,28 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"net"
-	"net/http"
-	"net/url"
-	"os"
 	"path"
-	"path/filepath"
 	"sort"
-	"time"
 
 	"golang.org/x/net/context"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
-	"github.com/docker/distribution/registry/client/auth"
-	"github.com/docker/distribution/registry/client/auth/challenge"
-	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/api/types"
-	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/cli/command"
-	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/cli/trust"
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
-	"github.com/docker/go-connections/tlsconfig"
-	"github.com/docker/notary"
 	"github.com/docker/notary/client"
-	"github.com/docker/notary/passphrase"
-	"github.com/docker/notary/storage"
-	"github.com/docker/notary/trustmanager"
-	"github.com/docker/notary/trustpinning"
 	"github.com/docker/notary/tuf/data"
-	"github.com/docker/notary/tuf/signed"
-)
-
-var (
-	releasesRole = path.Join(data.CanonicalTargetsRole, "releases")
 )
 
 type target struct {
-	reference registry.Reference
-	digest    digest.Digest
-	size      int64
+	name   string
+	digest digest.Digest
+	size   int64
 }
 
 // trustedPush handles content trust pushing of an image
@@ -81,7 +60,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 				target = nil
 				return
 			}
-			target.Name = registry.ParseReference(pushResult.Tag).String()
+			target.Name = pushResult.Tag
 			target.Hashes = data.Hashes{string(pushResult.Digest.Algorithm()): h}
 			target.Length = int64(pushResult.Size)
 		}
@@ -93,11 +72,9 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 		return errors.New("cannot push a digest reference")
 	case reference.NamedTagged:
 		tag = x.Tag()
-	}
-
-	// We want trust signatures to always take an explicit tag,
-	// otherwise it will act as an untrusted push.
-	if tag == "" {
+	default:
+		// We want trust signatures to always take an explicit tag,
+		// otherwise it will act as an untrusted push.
 		if err = jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil); err != nil {
 			return err
 		}
@@ -120,7 +97,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 
 	fmt.Fprintln(cli.Out(), "Signing and pushing trust metadata")
 
-	repo, err := GetNotaryRepository(cli, repoInfo, authConfig, "push", "pull")
+	repo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "push", "pull")
 	if err != nil {
 		fmt.Fprintf(cli.Out(), "Error establishing connection to notary repository: %s\n", err)
 		return err
@@ -147,7 +124,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 
 		// Initialize the notary repository with a remotely managed snapshot key
 		if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
-			return notaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(repoInfo.FullName(), err)
 		}
 		fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.FullName())
 		err = repo.AddTarget(target, data.CanonicalTargetsRole)
@@ -155,7 +132,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 		// already initialized and we have successfully downloaded the latest metadata
 		err = addTargetToAllSignableRoles(repo, target)
 	default:
-		return notaryError(repoInfo.FullName(), err)
+		return trust.NotaryError(repoInfo.FullName(), err)
 	}
 
 	if err == nil {
@@ -164,7 +141,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 
 	if err != nil {
 		fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
-		return notaryError(repoInfo.FullName(), err)
+		return trust.NotaryError(repoInfo.FullName(), err)
 	}
 
 	fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
@@ -234,20 +211,20 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
 }
 
 // trustedPull handles content trust pulling of an image
-func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
+func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
 	var refs []target
 
-	notaryRepo, err := GetNotaryRepository(cli, repoInfo, authConfig, "pull")
+	notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
 	if err != nil {
 		fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
 		return err
 	}
 
-	if ref.String() == "" {
+	if tagged, isTagged := ref.(reference.NamedTagged); !isTagged {
 		// List all targets
-		targets, err := notaryRepo.ListTargets(releasesRole, data.CanonicalTargetsRole)
+		targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole)
 		if err != nil {
-			return notaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(repoInfo.FullName(), err)
 		}
 		for _, tgt := range targets {
 			t, err := convertTarget(tgt.Target)
@@ -257,23 +234,23 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 			}
 			// Only list tags in the top level targets role or the releases delegation role - ignore
 			// all other delegation roles
-			if tgt.Role != releasesRole && tgt.Role != data.CanonicalTargetsRole {
+			if tgt.Role != trust.ReleasesRole && tgt.Role != data.CanonicalTargetsRole {
 				continue
 			}
 			refs = append(refs, t)
 		}
 		if len(refs) == 0 {
-			return notaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
+			return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
 		}
 	} else {
-		t, err := notaryRepo.GetTargetByName(ref.String(), releasesRole, data.CanonicalTargetsRole)
+		t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 		if err != nil {
-			return notaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(repoInfo.FullName(), err)
 		}
 		// Only get the tag if it's in the top level targets role or the releases delegation role
 		// ignore it if it's in any other delegation roles
-		if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
-			return notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String()))
+		if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
+			return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", tagged.Tag()))
 		}
 
 		logrus.Debugf("retrieving target for %s role\n", t.Role)
@@ -286,7 +263,7 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 	}
 
 	for i, r := range refs {
-		displayTag := r.reference.String()
+		displayTag := r.name
 		if displayTag != "" {
 			displayTag = ":" + displayTag
 		}
@@ -300,19 +277,16 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 			return err
 		}
 
-		// If reference is not trusted, tag by trusted reference
-		if !r.reference.HasDigest() {
-			tagged, err := reference.WithTag(repoInfo, r.reference.String())
-			if err != nil {
-				return err
-			}
-			trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
-			if err != nil {
-				return err
-			}
-			if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil {
-				return err
-			}
+		tagged, err := reference.WithTag(repoInfo, r.name)
+		if err != nil {
+			return err
+		}
+		trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
+		if err != nil {
+			return err
+		}
+		if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil {
+			return err
 		}
 	}
 	return nil
@@ -340,159 +314,6 @@ func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
 	return jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil)
 }
 
-func trustDirectory() string {
-	return filepath.Join(cliconfig.ConfigDir(), "trust")
-}
-
-// certificateDirectory returns the directory containing
-// TLS certificates for the given server. An error is
-// returned if there was an error parsing the server string.
-func certificateDirectory(server string) (string, error) {
-	u, err := url.Parse(server)
-	if err != nil {
-		return "", err
-	}
-
-	return filepath.Join(cliconfig.ConfigDir(), "tls", u.Host), nil
-}
-
-func trustServer(index *registrytypes.IndexInfo) (string, error) {
-	if s := os.Getenv("DOCKER_CONTENT_TRUST_SERVER"); s != "" {
-		urlObj, err := url.Parse(s)
-		if err != nil || urlObj.Scheme != "https" {
-			return "", fmt.Errorf("valid https URL required for trust server, got %s", s)
-		}
-
-		return s, nil
-	}
-	if index.Official {
-		return registry.NotaryServer, nil
-	}
-	return "https://" + index.Name, nil
-}
-
-type simpleCredentialStore struct {
-	auth types.AuthConfig
-}
-
-func (scs simpleCredentialStore) Basic(u *url.URL) (string, string) {
-	return scs.auth.Username, scs.auth.Password
-}
-
-func (scs simpleCredentialStore) RefreshToken(u *url.URL, service string) string {
-	return scs.auth.IdentityToken
-}
-
-func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {
-}
-
-// GetNotaryRepository returns a NotaryRepository which stores all the
-// information needed to operate on a notary repository.
-// It creates an HTTP transport providing authentication support.
-// TODO: move this too
-func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig, actions ...string) (*client.NotaryRepository, error) {
-	server, err := trustServer(repoInfo.Index)
-	if err != nil {
-		return nil, err
-	}
-
-	var cfg = tlsconfig.ClientDefault()
-	cfg.InsecureSkipVerify = !repoInfo.Index.Secure
-
-	// Get certificate base directory
-	certDir, err := certificateDirectory(server)
-	if err != nil {
-		return nil, err
-	}
-	logrus.Debugf("reading certificate directory: %s", certDir)
-
-	if err := registry.ReadCertsDirectory(cfg, certDir); err != nil {
-		return nil, err
-	}
-
-	base := &http.Transport{
-		Proxy: http.ProxyFromEnvironment,
-		Dial: (&net.Dialer{
-			Timeout:   30 * time.Second,
-			KeepAlive: 30 * time.Second,
-			DualStack: true,
-		}).Dial,
-		TLSHandshakeTimeout: 10 * time.Second,
-		TLSClientConfig:     cfg,
-		DisableKeepAlives:   true,
-	}
-
-	// Skip configuration headers since request is not going to Docker daemon
-	modifiers := registry.DockerHeaders(command.UserAgent(), http.Header{})
-	authTransport := transport.NewTransport(base, modifiers...)
-	pingClient := &http.Client{
-		Transport: authTransport,
-		Timeout:   5 * time.Second,
-	}
-	endpointStr := server + "/v2/"
-	req, err := http.NewRequest("GET", endpointStr, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	challengeManager := challenge.NewSimpleManager()
-
-	resp, err := pingClient.Do(req)
-	if err != nil {
-		// Ignore error on ping to operate in offline mode
-		logrus.Debugf("Error pinging notary server %q: %s", endpointStr, err)
-	} else {
-		defer resp.Body.Close()
-
-		// Add response to the challenge manager to parse out
-		// authentication header and register authentication method
-		if err := challengeManager.AddResponse(resp); err != nil {
-			return nil, err
-		}
-	}
-
-	creds := simpleCredentialStore{auth: authConfig}
-	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), actions...)
-	basicHandler := auth.NewBasicHandler(creds)
-	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
-	tr := transport.NewTransport(base, modifiers...)
-
-	return client.NewNotaryRepository(
-		trustDirectory(),
-		repoInfo.FullName(),
-		server,
-		tr,
-		getPassphraseRetriever(streams),
-		trustpinning.TrustPinConfig{})
-}
-
-func getPassphraseRetriever(streams command.Streams) notary.PassRetriever {
-	aliasMap := map[string]string{
-		"root":     "root",
-		"snapshot": "repository",
-		"targets":  "repository",
-		"default":  "repository",
-	}
-	baseRetriever := passphrase.PromptRetrieverWithInOut(streams.In(), streams.Out(), aliasMap)
-	env := map[string]string{
-		"root":     os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
-		"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
-		"targets":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
-		"default":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
-	}
-
-	return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
-		if v := env[alias]; v != "" {
-			return v, numAttempts > 1, nil
-		}
-		// For non-root roles, we can also try the "default" alias if it is specified
-		if v := env["default"]; v != "" && alias != data.CanonicalRootRole {
-			return v, numAttempts > 1, nil
-		}
-		return baseRetriever(keyName, alias, createNew, numAttempts)
-	}
-}
-
 // TrustedReference returns the canonical trusted reference for an image reference
 func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) {
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
@@ -503,20 +324,20 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
 	// Resolve the Auth config relevant for this server
 	authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index)
 
-	notaryRepo, err := GetNotaryRepository(cli, repoInfo, authConfig, "pull")
+	notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
 	if err != nil {
 		fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
 		return nil, err
 	}
 
-	t, err := notaryRepo.GetTargetByName(ref.Tag(), releasesRole, data.CanonicalTargetsRole)
+	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	if err != nil {
 		return nil, err
 	}
 	// Only list tags in the top level targets role or the releases delegation role - ignore
 	// all other delegation roles
-	if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
-		return nil, notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
+	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
+		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
 	}
 	r, err := convertTarget(t.Target)
 	if err != nil {
@@ -533,9 +354,9 @@ func convertTarget(t client.Target) (target, error) {
 		return target{}, errors.New("no valid hash, expecting sha256")
 	}
 	return target{
-		reference: registry.ParseReference(t.Name),
-		digest:    digest.NewDigestFromHex("sha256", hex.EncodeToString(h)),
-		size:      t.Length,
+		name:   t.Name,
+		digest: digest.NewDigestFromHex("sha256", hex.EncodeToString(h)),
+		size:   t.Length,
 	}, nil
 }
 
@@ -545,34 +366,3 @@ func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef referenc
 
 	return cli.Client().ImageTag(ctx, trustedRef.String(), ref.String())
 }
-
-// notaryError formats an error message received from the notary service
-func notaryError(repoName string, err error) error {
-	switch err.(type) {
-	case *json.SyntaxError:
-		logrus.Debugf("Notary syntax error: %s", err)
-		return fmt.Errorf("Error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address?", repoName)
-	case signed.ErrExpired:
-		return fmt.Errorf("Error: remote repository %s out-of-date: %v", repoName, err)
-	case trustmanager.ErrKeyNotFound:
-		return fmt.Errorf("Error: signing keys for remote repository %s not found: %v", repoName, err)
-	case storage.NetworkError:
-		return fmt.Errorf("Error: error contacting notary server: %v", err)
-	case storage.ErrMetaNotFound:
-		return fmt.Errorf("Error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err)
-	case trustpinning.ErrRootRotationFail, trustpinning.ErrValidationFail, signed.ErrInvalidKeyType:
-		return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
-	case signed.ErrNoKeys:
-		return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
-	case signed.ErrLowVersion:
-		return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
-	case signed.ErrRoleThreshold:
-		return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
-	case client.ErrRepositoryNotExist:
-		return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
-	case signed.ErrInsufficientSignatures:
-		return fmt.Errorf("Error: could not produce valid signature for %s.  If Yubikey was used, was touch input provided?: %v", repoName, err)
-	}
-
-	return err
-}
diff --git a/cli/command/image/trust_test.go b/cli/command/image/trust_test.go
index ba6373f2da..78146465e6 100644
--- a/cli/command/image/trust_test.go
+++ b/cli/command/image/trust_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	registrytypes "github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/cli/trust"
 	"github.com/docker/docker/registry"
 )
 
@@ -19,7 +20,7 @@ func TestENVTrustServer(t *testing.T) {
 	if err := os.Setenv("DOCKER_CONTENT_TRUST_SERVER", "https://notary-test.com:5000"); err != nil {
 		t.Fatal("Failed to set ENV variable")
 	}
-	output, err := trustServer(indexInfo)
+	output, err := trust.Server(indexInfo)
 	expectedStr := "https://notary-test.com:5000"
 	if err != nil || output != expectedStr {
 		t.Fatalf("Expected server to be %s, got %s", expectedStr, output)
@@ -32,7 +33,7 @@ func TestHTTPENVTrustServer(t *testing.T) {
 	if err := os.Setenv("DOCKER_CONTENT_TRUST_SERVER", "http://notary-test.com:5000"); err != nil {
 		t.Fatal("Failed to set ENV variable")
 	}
-	_, err := trustServer(indexInfo)
+	_, err := trust.Server(indexInfo)
 	if err == nil {
 		t.Fatal("Expected error with invalid scheme")
 	}
@@ -40,7 +41,7 @@ func TestHTTPENVTrustServer(t *testing.T) {
 
 func TestOfficialTrustServer(t *testing.T) {
 	indexInfo := &registrytypes.IndexInfo{Name: "testserver", Official: true}
-	output, err := trustServer(indexInfo)
+	output, err := trust.Server(indexInfo)
 	if err != nil || output != registry.NotaryServer {
 		t.Fatalf("Expected server to be %s, got %s", registry.NotaryServer, output)
 	}
@@ -48,7 +49,7 @@ func TestOfficialTrustServer(t *testing.T) {
 
 func TestNonOfficialTrustServer(t *testing.T) {
 	indexInfo := &registrytypes.IndexInfo{Name: "testserver", Official: false}
-	output, err := trustServer(indexInfo)
+	output, err := trust.Server(indexInfo)
 	expectedStr := "https://" + indexInfo.Name
 	if err != nil || output != expectedStr {
 		t.Fatalf("Expected server to be %s, got %s", expectedStr, output)
diff --git a/cli/command/service/create.go b/cli/command/service/create.go
index a8382835a0..ca2bb089fd 100644
--- a/cli/command/service/create.go
+++ b/cli/command/service/create.go
@@ -72,6 +72,10 @@ func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error {
 
 	ctx := context.Background()
 
+	if err := resolveServiceImageDigest(dockerCli, &service); err != nil {
+		return err
+	}
+
 	// only send auth if flag was set
 	if opts.registryAuth {
 		// Retrieve encoded auth token from the image reference
diff --git a/cli/command/service/trust.go b/cli/command/service/trust.go
new file mode 100644
index 0000000000..052d49c32a
--- /dev/null
+++ b/cli/command/service/trust.go
@@ -0,0 +1,96 @@
+package service
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/digest"
+	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/cli/command"
+	"github.com/docker/docker/cli/trust"
+	"github.com/docker/docker/reference"
+	"github.com/docker/docker/registry"
+	"github.com/docker/notary/tuf/data"
+	"github.com/pkg/errors"
+	"golang.org/x/net/context"
+)
+
+func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.ServiceSpec) error {
+	if !command.IsTrusted() {
+		// Digests are resolved by the daemon when not using content
+		// trust.
+		return nil
+	}
+
+	image := service.TaskTemplate.ContainerSpec.Image
+
+	// We only attempt to resolve the digest if the reference
+	// could be parsed as a digest reference. Specifying an image ID
+	// is valid but not resolvable. There is no warning message for
+	// an image ID because it's valid to use one.
+	if _, err := digest.ParseDigest(image); err == nil {
+		return nil
+	}
+
+	ref, err := reference.ParseNamed(image)
+	if err != nil {
+		return fmt.Errorf("Could not parse image reference %s", service.TaskTemplate.ContainerSpec.Image)
+	}
+	if _, ok := ref.(reference.Canonical); !ok {
+		ref = reference.WithDefaultTag(ref)
+
+		taggedRef, ok := ref.(reference.NamedTagged)
+		if !ok {
+			// This should never happen because a reference either
+			// has a digest, or WithDefaultTag would give it a tag.
+			return errors.New("Failed to resolve image digest using content trust: reference is missing a tag")
+		}
+
+		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
+		if err != nil {
+			return fmt.Errorf("Failed to resolve image digest using content trust: %v", err)
+		}
+		logrus.Debugf("resolved image tag to %s using content trust", resolvedImage.String())
+		service.TaskTemplate.ContainerSpec.Image = resolvedImage.String()
+	}
+	return nil
+}
+
+func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (distreference.Canonical, error) {
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index)
+
+	notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
+	if err != nil {
+		return nil, errors.Wrap(err, "error establishing connection to trust repository")
+	}
+
+	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
+	if err != nil {
+		return nil, trust.NotaryError(repoInfo.FullName(), err)
+	}
+	// Only get the tag if it's in the top level targets role or the releases delegation role
+	// ignore it if it's in any other delegation roles
+	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
+		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String()))
+	}
+
+	logrus.Debugf("retrieving target for %s role\n", t.Role)
+	h, ok := t.Hashes["sha256"]
+	if !ok {
+		return nil, errors.New("no valid hash, expecting sha256")
+	}
+
+	dgst := digest.NewDigestFromHex("sha256", hex.EncodeToString(h))
+
+	// Using distribution reference package to make sure that adding a
+	// digest does not erase the tag. When the two reference packages
+	// are unified, this will no longer be an issue.
+	return distreference.WithDigest(ref, dgst)
+}
diff --git a/cli/command/service/update.go b/cli/command/service/update.go
index 4bbcf35a8d..514b1bd510 100644
--- a/cli/command/service/update.go
+++ b/cli/command/service/update.go
@@ -103,6 +103,12 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str
 		return err
 	}
 
+	if flags.Changed("image") {
+		if err := resolveServiceImageDigest(dockerCli, spec); err != nil {
+			return err
+		}
+	}
+
 	updatedSecrets, err := getUpdatedSecrets(apiClient, flags, spec.TaskTemplate.ContainerSpec.Secrets)
 	if err != nil {
 		return err
diff --git a/cli/trust/trust.go b/cli/trust/trust.go
new file mode 100644
index 0000000000..0f3482f2d7
--- /dev/null
+++ b/cli/trust/trust.go
@@ -0,0 +1,221 @@
+package trust
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"path"
+	"path/filepath"
+	"time"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/registry/client/auth"
+	"github.com/docker/distribution/registry/client/auth/challenge"
+	"github.com/docker/distribution/registry/client/transport"
+	"github.com/docker/docker/api/types"
+	registrytypes "github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/cli/command"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/registry"
+	"github.com/docker/go-connections/tlsconfig"
+	"github.com/docker/notary"
+	"github.com/docker/notary/client"
+	"github.com/docker/notary/passphrase"
+	"github.com/docker/notary/storage"
+	"github.com/docker/notary/trustmanager"
+	"github.com/docker/notary/trustpinning"
+	"github.com/docker/notary/tuf/data"
+	"github.com/docker/notary/tuf/signed"
+)
+
+var (
+	// ReleasesRole is the role named "releases"
+	ReleasesRole = path.Join(data.CanonicalTargetsRole, "releases")
+)
+
+func trustDirectory() string {
+	return filepath.Join(cliconfig.ConfigDir(), "trust")
+}
+
+// certificateDirectory returns the directory containing
+// TLS certificates for the given server. An error is
+// returned if there was an error parsing the server string.
+func certificateDirectory(server string) (string, error) {
+	u, err := url.Parse(server)
+	if err != nil {
+		return "", err
+	}
+
+	return filepath.Join(cliconfig.ConfigDir(), "tls", u.Host), nil
+}
+
+// Server returns the base URL for the trust server.
+func Server(index *registrytypes.IndexInfo) (string, error) {
+	if s := os.Getenv("DOCKER_CONTENT_TRUST_SERVER"); s != "" {
+		urlObj, err := url.Parse(s)
+		if err != nil || urlObj.Scheme != "https" {
+			return "", fmt.Errorf("valid https URL required for trust server, got %s", s)
+		}
+
+		return s, nil
+	}
+	if index.Official {
+		return registry.NotaryServer, nil
+	}
+	return "https://" + index.Name, nil
+}
+
+type simpleCredentialStore struct {
+	auth types.AuthConfig
+}
+
+func (scs simpleCredentialStore) Basic(u *url.URL) (string, string) {
+	return scs.auth.Username, scs.auth.Password
+}
+
+func (scs simpleCredentialStore) RefreshToken(u *url.URL, service string) string {
+	return scs.auth.IdentityToken
+}
+
+func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {
+}
+
+// GetNotaryRepository returns a NotaryRepository which stores all the
+// information needed to operate on a notary repository.
+// It creates an HTTP transport providing authentication support.
+func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig, actions ...string) (*client.NotaryRepository, error) {
+	server, err := Server(repoInfo.Index)
+	if err != nil {
+		return nil, err
+	}
+
+	var cfg = tlsconfig.ClientDefault()
+	cfg.InsecureSkipVerify = !repoInfo.Index.Secure
+
+	// Get certificate base directory
+	certDir, err := certificateDirectory(server)
+	if err != nil {
+		return nil, err
+	}
+	logrus.Debugf("reading certificate directory: %s", certDir)
+
+	if err := registry.ReadCertsDirectory(cfg, certDir); err != nil {
+		return nil, err
+	}
+
+	base := &http.Transport{
+		Proxy: http.ProxyFromEnvironment,
+		Dial: (&net.Dialer{
+			Timeout:   30 * time.Second,
+			KeepAlive: 30 * time.Second,
+			DualStack: true,
+		}).Dial,
+		TLSHandshakeTimeout: 10 * time.Second,
+		TLSClientConfig:     cfg,
+		DisableKeepAlives:   true,
+	}
+
+	// Skip configuration headers since request is not going to Docker daemon
+	modifiers := registry.DockerHeaders(command.UserAgent(), http.Header{})
+	authTransport := transport.NewTransport(base, modifiers...)
+	pingClient := &http.Client{
+		Transport: authTransport,
+		Timeout:   5 * time.Second,
+	}
+	endpointStr := server + "/v2/"
+	req, err := http.NewRequest("GET", endpointStr, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	challengeManager := challenge.NewSimpleManager()
+
+	resp, err := pingClient.Do(req)
+	if err != nil {
+		// Ignore error on ping to operate in offline mode
+		logrus.Debugf("Error pinging notary server %q: %s", endpointStr, err)
+	} else {
+		defer resp.Body.Close()
+
+		// Add response to the challenge manager to parse out
+		// authentication header and register authentication method
+		if err := challengeManager.AddResponse(resp); err != nil {
+			return nil, err
+		}
+	}
+
+	creds := simpleCredentialStore{auth: authConfig}
+	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), actions...)
+	basicHandler := auth.NewBasicHandler(creds)
+	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
+	tr := transport.NewTransport(base, modifiers...)
+
+	return client.NewNotaryRepository(
+		trustDirectory(),
+		repoInfo.FullName(),
+		server,
+		tr,
+		getPassphraseRetriever(streams),
+		trustpinning.TrustPinConfig{})
+}
+
+func getPassphraseRetriever(streams command.Streams) notary.PassRetriever {
+	aliasMap := map[string]string{
+		"root":     "root",
+		"snapshot": "repository",
+		"targets":  "repository",
+		"default":  "repository",
+	}
+	baseRetriever := passphrase.PromptRetrieverWithInOut(streams.In(), streams.Out(), aliasMap)
+	env := map[string]string{
+		"root":     os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
+		"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
+		"targets":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
+		"default":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
+	}
+
+	return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
+		if v := env[alias]; v != "" {
+			return v, numAttempts > 1, nil
+		}
+		// For non-root roles, we can also try the "default" alias if it is specified
+		if v := env["default"]; v != "" && alias != data.CanonicalRootRole {
+			return v, numAttempts > 1, nil
+		}
+		return baseRetriever(keyName, alias, createNew, numAttempts)
+	}
+}
+
+// NotaryError formats an error message received from the notary service
+func NotaryError(repoName string, err error) error {
+	switch err.(type) {
+	case *json.SyntaxError:
+		logrus.Debugf("Notary syntax error: %s", err)
+		return fmt.Errorf("Error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address?", repoName)
+	case signed.ErrExpired:
+		return fmt.Errorf("Error: remote repository %s out-of-date: %v", repoName, err)
+	case trustmanager.ErrKeyNotFound:
+		return fmt.Errorf("Error: signing keys for remote repository %s not found: %v", repoName, err)
+	case storage.NetworkError:
+		return fmt.Errorf("Error: error contacting notary server: %v", err)
+	case storage.ErrMetaNotFound:
+		return fmt.Errorf("Error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err)
+	case trustpinning.ErrRootRotationFail, trustpinning.ErrValidationFail, signed.ErrInvalidKeyType:
+		return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
+	case signed.ErrNoKeys:
+		return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
+	case signed.ErrLowVersion:
+		return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
+	case signed.ErrRoleThreshold:
+		return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
+	case client.ErrRepositoryNotExist:
+		return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
+	case signed.ErrInsufficientSignatures:
+		return fmt.Errorf("Error: could not produce valid signature for %s.  If Yubikey was used, was touch input provided?: %v", repoName, err)
+	}
+
+	return err
+}
diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go
index b2afe15eef..21cd7f63a6 100644
--- a/daemon/cluster/cluster.go
+++ b/daemon/cluster/cluster.go
@@ -1111,9 +1111,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apity
 		if err != nil {
 			logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error())
 			resp.Warnings = append(resp.Warnings, fmt.Sprintf("unable to pin image %s to digest: %s", ctnr.Image, err.Error()))
-		} else {
+		} else if ctnr.Image != digestImage {
 			logrus.Debugf("pinning image %s by digest: %s", ctnr.Image, digestImage)
 			ctnr.Image = digestImage
+		} else {
+			logrus.Debugf("creating service using supplied digest reference %s", ctnr.Image)
 		}
 	}
 
@@ -1223,6 +1225,8 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 		} else if newCtnr.Image != digestImage {
 			logrus.Debugf("pinning image %s by digest: %s", newCtnr.Image, digestImage)
 			newCtnr.Image = digestImage
+		} else {
+			logrus.Debugf("updating service using supplied digest reference %s", newCtnr.Image)
 		}
 	}
 
diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go
index 81f458f5ae..7084d6f8af 100644
--- a/integration-cli/check_test.go
+++ b/integration-cli/check_test.go
@@ -348,3 +348,36 @@ func (s *DockerTrustSuite) TearDownTest(c *check.C) {
 	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
 	s.ds.TearDownTest(c)
 }
+
+func init() {
+	ds := &DockerSuite{}
+	check.Suite(&DockerTrustedSwarmSuite{
+		trustSuite: DockerTrustSuite{
+			ds: ds,
+		},
+		swarmSuite: DockerSwarmSuite{
+			ds: ds,
+		},
+	})
+}
+
+type DockerTrustedSwarmSuite struct {
+	swarmSuite DockerSwarmSuite
+	trustSuite DockerTrustSuite
+	reg        *testRegistryV2
+	not        *testNotary
+}
+
+func (s *DockerTrustedSwarmSuite) SetUpTest(c *check.C) {
+	s.swarmSuite.SetUpTest(c)
+	s.trustSuite.SetUpTest(c)
+}
+
+func (s *DockerTrustedSwarmSuite) TearDownTest(c *check.C) {
+	s.trustSuite.TearDownTest(c)
+	s.swarmSuite.TearDownTest(c)
+}
+
+func (s *DockerTrustedSwarmSuite) OnTimeout(c *check.C) {
+	s.swarmSuite.OnTimeout(c)
+}
diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go
index b0f2de7a0a..376f366782 100644
--- a/integration-cli/docker_cli_swarm_test.go
+++ b/integration-cli/docker_cli_swarm_test.go
@@ -1085,3 +1085,84 @@ func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) {
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "map[foo:bar]")
 }
+
+func (s *DockerTrustedSwarmSuite) TestTrustedServiceCreate(c *check.C) {
+	d := s.swarmSuite.AddDaemon(c, true, true)
+
+	// Attempt creating a service from an image that is known to notary.
+	repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
+
+	name := "trusted"
+	serviceCmd := d.command("-D", "service", "create", "--name", name, repoName, "top")
+	s.trustSuite.trustedCmd(serviceCmd)
+	out, _, err := runCommandWithOutput(serviceCmd)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, "resolved image tag to", check.Commentf(out))
+
+	out, err = d.Cmd("service", "inspect", "--pretty", name)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out))
+
+	// Try trusted service create on an untrusted tag.
+
+	repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
+	// tag the image and upload it to the private registry
+	dockerCmd(c, "tag", "busybox", repoName)
+	dockerCmd(c, "push", repoName)
+	dockerCmd(c, "rmi", repoName)
+
+	name = "untrusted"
+	serviceCmd = d.command("service", "create", "--name", name, repoName, "top")
+	s.trustSuite.trustedCmd(serviceCmd)
+	out, _, err = runCommandWithOutput(serviceCmd)
+
+	c.Assert(err, check.NotNil, check.Commentf(out))
+	c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
+
+	out, err = d.Cmd("service", "inspect", "--pretty", name)
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+}
+
+func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) {
+	d := s.swarmSuite.AddDaemon(c, true, true)
+
+	// Attempt creating a service from an image that is known to notary.
+	repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
+
+	name := "myservice"
+
+	// Create a service without content trust
+	_, err := d.Cmd("service", "create", "--name", name, repoName, "top")
+	c.Assert(err, checker.IsNil)
+
+	out, err := d.Cmd("service", "inspect", "--pretty", name)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	// Daemon won't insert the digest because this is disabled by
+	// DOCKER_SERVICE_PREFER_OFFLINE_IMAGE.
+	c.Assert(out, check.Not(checker.Contains), repoName+"@", check.Commentf(out))
+
+	serviceCmd := d.command("-D", "service", "update", "--image", repoName, name)
+	s.trustSuite.trustedCmd(serviceCmd)
+	out, _, err = runCommandWithOutput(serviceCmd)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, "resolved image tag to", check.Commentf(out))
+
+	out, err = d.Cmd("service", "inspect", "--pretty", name)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out))
+
+	// Try trusted service update on an untrusted tag.
+
+	repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
+	// tag the image and upload it to the private registry
+	dockerCmd(c, "tag", "busybox", repoName)
+	dockerCmd(c, "push", repoName)
+	dockerCmd(c, "rmi", repoName)
+
+	serviceCmd = d.command("service", "update", "--image", repoName, name)
+	s.trustSuite.trustedCmd(serviceCmd)
+	out, _, err = runCommandWithOutput(serviceCmd)
+
+	c.Assert(err, check.NotNil, check.Commentf(out))
+	c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
+}
diff --git a/registry/reference.go b/registry/reference.go
deleted file mode 100644
index e15f83eeeb..0000000000
--- a/registry/reference.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package registry
-
-import (
-	"strings"
-
-	"github.com/docker/distribution/digest"
-)
-
-// Reference represents a tag or digest within a repository
-type Reference interface {
-	// HasDigest returns whether the reference has a verifiable
-	// content addressable reference which may be considered secure.
-	HasDigest() bool
-
-	// ImageName returns an image name for the given repository
-	ImageName(string) string
-
-	// Returns a string representation of the reference
-	String() string
-}
-
-type tagReference struct {
-	tag string
-}
-
-func (tr tagReference) HasDigest() bool {
-	return false
-}
-
-func (tr tagReference) ImageName(repo string) string {
-	return repo + ":" + tr.tag
-}
-
-func (tr tagReference) String() string {
-	return tr.tag
-}
-
-type digestReference struct {
-	digest digest.Digest
-}
-
-func (dr digestReference) HasDigest() bool {
-	return true
-}
-
-func (dr digestReference) ImageName(repo string) string {
-	return repo + "@" + dr.String()
-}
-
-func (dr digestReference) String() string {
-	return dr.digest.String()
-}
-
-// ParseReference parses a reference into either a digest or tag reference
-func ParseReference(ref string) Reference {
-	if strings.Contains(ref, ":") {
-		dgst, err := digest.ParseDigest(ref)
-		if err == nil {
-			return digestReference{digest: dgst}
-		}
-	}
-	return tagReference{tag: ref}
-}
-
-// DigestReference creates a digest reference using a digest
-func DigestReference(dgst digest.Digest) Reference {
-	return digestReference{digest: dgst}
-}