Repository: smallstep/crypto
Pull request #85 (2022-10-04 01:03:34)

Add package fingerprint
### Description

This PR adds the new `fingerprint` package that unifies the logic for X.509 and SSH fingerprint, allowing at the same time supporting other hash functions required by `step`.


Patch:
diff --git a/README.md b/README.md
index 763c827..e91f391 100644
--- a/README.md
+++ b/README.md
@@ -75,3 +75,8 @@ or just a YubiKey or an ssh-agent. On the cloud it supports:
 * [Amazon AWS KMS](https://aws.amazon.com/kms/)
 * [Google Cloud Key Management](https://cloud.google.com/security-key-management)
 * [Microsoft Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/)
+
+### fingerprint
+
+Package `fingerprint` provides methods for creating and encoding X.509
+certificate, SSH certificate and SSH key fingerprints.
diff --git a/fingerprint/fingerprint.go b/fingerprint/fingerprint.go
new file mode 100644
index 0000000..6eb174b
--- /dev/null
+++ b/fingerprint/fingerprint.go
@@ -0,0 +1,78 @@
+package fingerprint
+
+import (
+	"crypto"
+	"encoding/base64"
+	"encoding/hex"
+	"fmt"
+	"strings"
+
+	"go.step.sm/crypto/internal/emoji"
+)
+
+// Encoding defines the supported encodings for certificates and key
+// fingerprints.
+//
+// This type is the base for sshutil.FingerprintEncoding and
+// x509util.FingerprintEncoding types.
+type Encoding int
+
+const (
+	// HexFingerprint represents the hex encoding of the fingerprint.
+	//
+	// This is the default encoding for an X.509 certificate.
+	HexFingerprint Encoding = iota + 1
+	// Base64Fingerprint represents the base64 encoding of the fingerprint.
+	//
+	// This is the default encoding for a public key.
+	Base64Fingerprint
+	// Base64URLFingerprint represents the base64URL encoding of the fingerprint.
+	Base64URLFingerprint
+	// Base64RawFingerprint represents the base64RawStd encoding of the
+	// fingerprint.
+	//
+	// This is the default encoding for an SSH key and certificate.
+	Base64RawFingerprint
+	// Base64RawURLFingerprint represents the base64RawURL encoding of the fingerprint.
+	Base64RawURLFingerprint
+	// EmojiFingerprint represents the emoji encoding of the fingerprint.
+	EmojiFingerprint
+)
+
+// New creates a fingerprint of the given data by hashing it and returns it in
+// the encoding format.
+func New(data []byte, h crypto.Hash, encoding Encoding) (string, error) {
+	if !h.Available() {
+		return "", fmt.Errorf("hash function %q is not available", h.String())
+	}
+	hash := h.New()
+	if _, err := hash.Write(data); err != nil {
+		return "", fmt.Errorf("error creating hash: %w", err)
+	}
+	fp := Fingerprint(hash.Sum(nil), encoding)
+	if fp == "" {
+		return "", fmt.Errorf("unknown encoding value %d", encoding)
+	}
+	return fp, nil
+}
+
+// Fingerprint encodes the given digest using the encoding format. If an invalid
+// encoding is passed, the return value will be an empty string.
+func Fingerprint(digest []byte, encoding Encoding) string {
+	switch encoding {
+	case HexFingerprint:
+		return strings.ToLower(hex.EncodeToString(digest))
+	case Base64Fingerprint:
+		return base64.StdEncoding.EncodeToString(digest)
+	case Base64URLFingerprint:
+		return base64.URLEncoding.EncodeToString(digest)
+	case Base64RawFingerprint:
+		return base64.RawStdEncoding.EncodeToString(digest)
+	case Base64RawURLFingerprint:
+		return base64.RawURLEncoding.EncodeToString(digest)
+	case EmojiFingerprint:
+		return emoji.Emoji(digest)
+	default:
+		return ""
+	}
+}
diff --git a/sshutil/fingerprint.go b/sshutil/fingerprint.go
index d66cc98..0017584 100644
--- a/sshutil/fingerprint.go
+++ b/sshutil/fingerprint.go
@@ -4,68 +4,58 @@ import (
 	"crypto/dsa" //nolint:staticcheck // support for DSA fingerprints
 	"crypto/rsa"
 	"crypto/sha256"
-	"encoding/base64"
-	"encoding/hex"
 	"fmt"
-	"strings"
 
 	"github.com/pkg/errors"
-	"go.step.sm/crypto/internal/emoji"
+	"go.step.sm/crypto/fingerprint"
 	"golang.org/x/crypto/ssh"
 )
 
 // FingerprintEncoding defines the supported encodings for SSH key and
 // certificate fingerprints.
-type FingerprintEncoding int
+type FingerprintEncoding = fingerprint.Encoding
 
 // Supported fingerprint encodings.
 const (
-	// Base64RawFingerprint represents the base64RawStd encoding of the
-	// fingerprint. This is the default encoding for an SSH key.
-	Base64RawFingerprint FingerprintEncoding = iota
-	// Base64RawURLFingerprint represents the base64RawURL encoding of the
-	// fingerprint.
-	Base64RawURLFingerprint
-	// Base64Fingerprint represents the base64 encoding of the fingerprint.
-	Base64Fingerprint
-	// Base64UrlFingerprint represents the base64URL encoding of the
-	// fingerprint.
-	Base64UrlFingerprint FingerprintEncoding = iota
+	// DefaultFingerprint represents base64RawStd encoding of the fingerprint.
+	DefaultFingerprint = FingerprintEncoding(0)
 	// HexFingerprint represents the hex encoding of the fingerprint.
-	HexFingerprint
+	HexFingerprint = fingerprint.HexFingerprint
+	// Base64Fingerprint represents the base64 encoding of the fingerprint.
+	Base64Fingerprint = fingerprint.Base64Fingerprint
+	// Base64URLFingerprint represents the base64URL encoding of the fingerprint.
+	Base64URLFingerprint = fingerprint.Base64URLFingerprint
+	// Base64RawFingerprint represents the base64RawStd encoding of the fingerprint.
+	Base64RawFingerprint = fingerprint.Base64RawFingerprint
+	// Base64RawURLFingerprint represents the base64RawURL encoding of the fingerprint.
+	Base64RawURLFingerprint = fingerprint.Base64RawURLFingerprint
 	// EmojiFingerprint represents the emoji encoding of the fingerprint.
-	EmojiFingerprint
+	EmojiFingerprint = fingerprint.EmojiFingerprint
 )
 
 // Fingerprint returns the SHA-256 fingerprint of an ssh public key or
 // certificate.
 func Fingerprint(pub ssh.PublicKey) string {
-	return EncodedFingerprint(pub, Base64RawFingerprint)
+	return EncodedFingerprint(pub, DefaultFingerprint)
 }
 
 // EncodedFingerprint returns the SHA-256 hash of an ssh public key or
 // certificate using the specified encoding. If an invalid encoding is passed,
 // the return value will be an empty string.
 func EncodedFingerprint(pub ssh.PublicKey, encoding FingerprintEncoding) string {
-	const prefix = "SHA256:"
+	var fp string
 
 	sum := sha256.Sum256(pub.Marshal())
 	switch encoding {
-	case Base64RawFingerprint:
-		return prefix + base64.RawStdEncoding.EncodeToString(sum[:])
-	case Base64RawURLFingerprint:
-		return prefix + base64.RawURLEncoding.EncodeToString(sum[:])
-	case Base64Fingerprint:
-		return prefix + base64.StdEncoding.EncodeToString(sum[:])
-	case Base64UrlFingerprint:
-		return prefix + base64.URLEncoding.EncodeToString(sum[:])
-	case HexFingerprint:
-		return prefix + strings.ToLower(hex.EncodeToString(sum[:]))
-	case EmojiFingerprint:
-		return prefix + emoji.Emoji(sum[:])
+	case DefaultFingerprint:
+		fp = fingerprint.Fingerprint(sum[:], Base64RawFingerprint)
 	default:
+		fp = fingerprint.Fingerprint(sum[:], encoding)
+	}
+	if fp == "" {
 		return ""
 	}
+	return "SHA256:" + fp
 }
 
 // FormatFingerprint parses a public key from an authorized_keys file used in
diff --git a/x509util/fingerprint.go b/x509util/fingerprint.go
new file mode 100644
index 0000000..e75a6df
--- /dev/null
+++ b/x509util/fingerprint.go
@@ -0,0 +1,48 @@
+package x509util
+
+import (
+	"crypto/sha256"
+	"crypto/x509"
+
+	"go.step.sm/crypto/fingerprint"
+)
+
+// FingerprintEncoding defines the supported encodings in certificate
+// fingerprints.
+type FingerprintEncoding = fingerprint.Encoding
+
+// Supported fingerprint encodings.
+const (
+	// DefaultFingerprint represents the hex encoding of the fingerprint.
+	DefaultFingerprint = FingerprintEncoding(0)
+	// HexFingerprint represents the hex encoding of the fingerprint.
+	HexFingerprint = fingerprint.HexFingerprint
+	// Base64Fingerprint represents the base64 encoding of the fingerprint.
+	Base64Fingerprint = fingerprint.Base64Fingerprint
+	// Base64URLFingerprint represents the base64URL encoding of the fingerprint.
+	Base64URLFingerprint = fingerprint.Base64URLFingerprint
+	// Base64RawFingerprint represents the base64RawStd encoding of the fingerprint.
+	Base64RawFingerprint = fingerprint.Base64RawFingerprint
+	// Base64RawURLFingerprint represents the base64RawURL encoding of the fingerprint.
+	Base64RawURLFingerprint = fingerprint.Base64RawURLFingerprint
+	// EmojiFingerprint represents the emoji encoding of the fingerprint.
+	EmojiFingerprint = fingerprint.EmojiFingerprint
+)
+
+// Fingerprint returns the SHA-256 fingerprint of the certificate.
+func Fingerprint(cert *x509.Certificate) string {
+	return EncodedFingerprint(cert, DefaultFingerprint)
+}
+
+// EncodedFingerprint returns the SHA-256 hash of the certificate using the
+// specified encoding. If an invalid encoding is passed, the return value will
+// be an empty string.
+func EncodedFingerprint(cert *x509.Certificate, encoding FingerprintEncoding) string {
+	sum := sha256.Sum256(cert.Raw)
+	switch encoding {
+	case DefaultFingerprint:
+		return fingerprint.Fingerprint(sum[:], HexFingerprint)
+	default:
+		return fingerprint.Fingerprint(sum[:], encoding)
+	}
+}
diff --git a/x509util/utils.go b/x509util/utils.go
index a8575d9..b3961da 100644
--- a/x509util/utils.go
+++ b/x509util/utils.go
@@ -5,12 +5,9 @@ import (
 	"crypto"
 	"crypto/rand"
 	"crypto/sha1" //nolint:gosec // SubjectKeyIdentifier by RFC 5280
-	"crypto/sha256"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"encoding/asn1"
-	"encoding/base64"
-	"encoding/hex"
 	"math/big"
 	"net"
 	"net/url"
@@ -19,31 +16,9 @@ import (
 	"unicode/utf8"
 
 	"github.com/pkg/errors"
-	"go.step.sm/crypto/internal/emoji"
 	"golang.org/x/net/idna"
 )
 
-// FingerprintEncoding defines the supported encodings in certificate
-// fingerprints.
-type FingerprintEncoding int
-
-// Supported fingerprint encodings.
-const (
-	// HexFingerprint represents the hex encoding of the fingerprint. This is
-	// the default encoding for an X.509 certificate.
-	HexFingerprint FingerprintEncoding = iota
-	// Base64Fingerprint represents the base64 encoding of the fingerprint.
-	Base64Fingerprint
-	// Base64UrlFingerprint represents the base64URL encoding of the fingerprint.
-	Base64UrlFingerprint
-	// Base64RawFingerprint represents the base64RawStd encoding of the fingerprint.
-	Base64RawFingerprint
-	// Base64RawURLFingerprint represents the base64RawURL encoding of the fingerprint.
-	Base64RawURLFingerprint
-	// EmojiFingerprint represents the emoji encoding of the fingerprint.
-	EmojiFingerprint
-)
-
 var emptyASN1Subject = []byte{0x30, 0}
 
 // SanitizeName converts the given domain to its ASCII form.
@@ -113,34 +88,6 @@ func CreateSANs(sans []string) []SubjectAlternativeName {
 	return sanTypes
 }
 
-// Fingerprint returns the SHA-256 fingerprint of the certificate.
-func Fingerprint(cert *x509.Certificate) string {
-	return EncodedFingerprint(cert, HexFingerprint)
-}
-
-// EncodedFingerprint returns the SHA-256 hash of the certificate using the
-// specified encoding. If an invalid encoding is passed, the return value will
-// be an empty string.
-func EncodedFingerprint(cert *x509.Certificate, encoding FingerprintEncoding) string {
-	sum := sha256.Sum256(cert.Raw)
-	switch encoding {
-	case HexFingerprint:
-		return strings.ToLower(hex.EncodeToString(sum[:]))
-	case Base64Fingerprint:
-		return base64.StdEncoding.EncodeToString(sum[:])
-	case Base64UrlFingerprint:
-		return base64.URLEncoding.EncodeToString(sum[:])
-	case Base64RawFingerprint:
-		return base64.RawStdEncoding.EncodeToString(sum[:])
-	case Base64RawURLFingerprint:
-		return base64.RawURLEncoding.EncodeToString(sum[:])
-	case EmojiFingerprint:
-		return emoji.Emoji(sum[:])
-	default:
-		return ""
-	}
-}
-
 // generateSerialNumber returns a random serial number.
 func generateSerialNumber() (*big.Int, error) {
 	limit := new(big.Int).Lsh(big.NewInt(1), 128)


Test patch:
diff --git a/fingerprint/fingerprint_test.go b/fingerprint/fingerprint_test.go
new file mode 100644
index 0000000..ac09c45
--- /dev/null
+++ b/fingerprint/fingerprint_test.go
@@ -0,0 +1,87 @@
+package fingerprint
+
+import (
+	"crypto"
+	"errors"
+	"hash"
+	"testing"
+)
+
+type failHash struct{}
+
+func (h failHash) Write(b []byte) (int, error) {
+	return 0, errors.New("failed to write")
+}
+func (h failHash) Sum(b []byte) []byte { return nil }
+func (h failHash) Reset()              {}
+func (h failHash) Size() int           { return 32 }
+func (h failHash) BlockSize() int      { return 32 }
+
+func TestNew(t *testing.T) {
+	data := []byte(`Lorem ipsum dolor sit amet`)
+	// This overwrites MD4 with failHash
+	crypto.RegisterHash(1, func() hash.Hash {
+		return failHash{}
+	})
+	type args struct {
+		data     []byte
+		h        crypto.Hash
+		encoding Encoding
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    string
+		wantErr bool
+	}{
+		{"sha256", args{data, crypto.SHA256, HexFingerprint}, "16aba5393ad72c0041f5600ad3c2c52ec437a2f0c7fc08fadfc3c0fe9641d7a3", false},
+		{"unavailable", args{data, crypto.Hash(1000), HexFingerprint}, "", true},
+		{"fail encoding", args{data, crypto.SHA256, Encoding(1000)}, "", true},
+		{"fail write", args{data, crypto.Hash(1), HexFingerprint}, "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := New(tt.args.data, tt.args.h, tt.args.encoding)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("New() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestFingerprint(t *testing.T) {
+	digest := []byte{
+		0x38, 0x01, 0x16, 0x21, 0xec, 0xdc, 0xc2, 0x17,
+		0x2e, 0x93, 0x3a, 0x1e, 0xf2, 0x31, 0x7e, 0xfc,
+		0x53, 0x5a, 0x16, 0x1c, 0x00, 0x33, 0x3a, 0xee,
+		0x3f, 0x84, 0xab, 0xfa, 0xb4, 0xe6, 0x40, 0xbf,
+	}
+	type args struct {
+		digest   []byte
+		encoding Encoding
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{"HexFingerprint", args{digest, HexFingerprint}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
+		{"Base64Fingerprint", args{digest, Base64Fingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8="},
+		{"Base64URLFingerprint", args{digest, Base64URLFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8="},
+		{"Base64RawFingerprint", args{digest, Base64RawFingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8"},
+		{"Base64RawURLFingerprint", args{digest, Base64RawURLFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8"},
+		{"EmojiFingerprint", args{digest, EmojiFingerprint}, "💨🎱🌼📆🎠🎉🎿🚙🍪🌊♦️💡✌️🐮🔒❌🖕😬🌼👦👍👑♦️🇬🇧👂🔬📌♿🚀🚜🍆🐑"},
+		{"Unknown", args{digest, 0}, ""},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := Fingerprint(tt.args.digest, tt.args.encoding); got != tt.want {
+				t.Errorf("Fingerprint() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/sshutil/fingerprint_test.go b/sshutil/fingerprint_test.go
index b344b4b..f10394b 100644
--- a/sshutil/fingerprint_test.go
+++ b/sshutil/fingerprint_test.go
@@ -86,11 +86,11 @@ func TestEncodedFingerprint(t *testing.T) {
 		args args
 		want string
 	}{
-		{"ok", args{sshECKey, 0}, ssh.FingerprintSHA256(sshECKey)},
+		{"default", args{sshECKey, 0}, ssh.FingerprintSHA256(sshECKey)},
 		{"Base64RawFingerprint", args{sshECKey, Base64RawFingerprint}, expected},
 		{"Base64RawURLFingerprint", args{sshECKey, Base64RawURLFingerprint}, "SHA256:" + base64.RawURLEncoding.EncodeToString(b)},
 		{"Base64Fingerprint", args{sshECKey, Base64Fingerprint}, "SHA256:" + base64.StdEncoding.EncodeToString(b)},
-		{"Base64UrlFingerprint", args{sshECKey, Base64UrlFingerprint}, "SHA256:" + base64.URLEncoding.EncodeToString(b)},
+		{"Base64URLFingerprint", args{sshECKey, Base64URLFingerprint}, "SHA256:" + base64.URLEncoding.EncodeToString(b)},
 		{"HexFingerprint", args{sshECKey, HexFingerprint}, "SHA256:" + hex.EncodeToString(b)},
 		{"EmojiFingerprint", args{sshECKey, EmojiFingerprint}, "SHA256:" + emoji.Emoji(b)},
 		{"fail", args{sshECKey, 100}, ""},
@@ -169,7 +169,7 @@ func TestFormatFingerprint(t *testing.T) {
 		{"Base64RawFingerprint", args{marshal(sshECKey, ""), Base64RawFingerprint}, "256 " + ssh.FingerprintSHA256(sshECKey) + " no comment (ECDSA)", false},
 		{"Base64RawURLFingerprint", args{marshal(sshECKey, ""), Base64RawURLFingerprint}, "256 SHA256:" + base64.RawURLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false},
 		{"Base64Fingerprint", args{marshal(sshECKey, ""), Base64Fingerprint}, "256 SHA256:" + base64.StdEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false},
-		{"Base64UrlFingerprint", args{marshal(sshECKey, ""), Base64UrlFingerprint}, "256 SHA256:" + base64.URLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false},
+		{"Base64UrlFingerprint", args{marshal(sshECKey, ""), Base64URLFingerprint}, "256 SHA256:" + base64.URLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false},
 		{"HexFingerprint", args{marshal(sshECKey, ""), HexFingerprint}, "256 SHA256:" + hex.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false},
 		{"EmojiFingerprint", args{marshal(sshECKey, ""), EmojiFingerprint}, "256 SHA256:" + emoji.Emoji(ec256Bytes) + " no comment (ECDSA)", false},
 		{"fail input", args{marshal(sshECKey, "")[:50], EmojiFingerprint}, "", true},
diff --git a/x509util/fingerprint_test.go b/x509util/fingerprint_test.go
new file mode 100644
index 0000000..3126e99
--- /dev/null
+++ b/x509util/fingerprint_test.go
@@ -0,0 +1,62 @@
+package x509util
+
+import (
+	"crypto/x509"
+	"testing"
+)
+
+func TestFingerprint(t *testing.T) {
+	ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt")
+	rsaCrt := decodeCertificateFile(t, "testdata/smallstep.crt")
+	ed25519Crt := decodeCertificateFile(t, "testdata/ed25519.crt")
+
+	type args struct {
+		cert *x509.Certificate
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{"ecdsaCert", args{ecdsaCrt}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
+		{"rsaCert", args{rsaCrt}, "5eeaf6dd1d1f064f6f95c5d74c39ad0abca33bdba59d2844d0b5e6d8453f6c4b"},
+		{"ed25519Cert", args{ed25519Crt}, "047b2fff20997a5009d1b36864af95b03f168c09dc2ed1a71ee36ccf973c9d31"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := Fingerprint(tt.args.cert); got != tt.want {
+				t.Errorf("Fingerprint() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestEncodedFingerprint(t *testing.T) {
+	ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt")
+
+	type args struct {
+		cert     *x509.Certificate
+		encoding FingerprintEncoding
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{"default", args{ecdsaCrt, 0}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
+		{"HexFingerprint", args{ecdsaCrt, HexFingerprint}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
+		{"Base64Fingerprint", args{ecdsaCrt, Base64Fingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8="},
+		{"Base64URLFingerprint", args{ecdsaCrt, Base64URLFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8="},
+		{"Base64RawFingerprint", args{ecdsaCrt, Base64RawFingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8"},
+		{"Base64RawURLFingerprint", args{ecdsaCrt, Base64RawURLFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8"},
+		{"EmojiFingerprint", args{ecdsaCrt, EmojiFingerprint}, "💨🎱🌼📆🎠🎉🎿🚙🍪🌊♦️💡✌️🐮🔒❌🖕😬🌼👦👍👑♦️🇬🇧👂🔬📌♿🚀🚜🍆🐑"},
+		{"Unknown", args{ecdsaCrt, 100}, ""},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := EncodedFingerprint(tt.args.cert, tt.args.encoding); got != tt.want {
+				t.Errorf("EncodedFingerprint() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/x509util/utils_test.go b/x509util/utils_test.go
index b05e052..534b884 100644
--- a/x509util/utils_test.go
+++ b/x509util/utils_test.go
@@ -143,61 +143,6 @@ func TestCreateSANs(t *testing.T) {
 	}
 }
 
-func TestFingerprint(t *testing.T) {
-	ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt")
-	rsaCrt := decodeCertificateFile(t, "testdata/smallstep.crt")
-	ed25519Crt := decodeCertificateFile(t, "testdata/ed25519.crt")
-
-	type args struct {
-		cert *x509.Certificate
-	}
-	tests := []struct {
-		name string
-		args args
-		want string
-	}{
-		{"ecdsaCert", args{ecdsaCrt}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
-		{"rsaCert", args{rsaCrt}, "5eeaf6dd1d1f064f6f95c5d74c39ad0abca33bdba59d2844d0b5e6d8453f6c4b"},
-		{"ed25519Cert", args{ed25519Crt}, "047b2fff20997a5009d1b36864af95b03f168c09dc2ed1a71ee36ccf973c9d31"},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := Fingerprint(tt.args.cert); got != tt.want {
-				t.Errorf("Fingerprint() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestEncodedFingerprint(t *testing.T) {
-	ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt")
-
-	type args struct {
-		cert     *x509.Certificate
-		encoding FingerprintEncoding
-	}
-	tests := []struct {
-		name string
-		args args
-		want string
-	}{
-		{"HexFingerprint", args{ecdsaCrt, HexFingerprint}, "38011621ecdcc2172e933a1ef2317efc535a161c00333aee3f84abfab4e640bf"},
-		{"Base64Fingerprint", args{ecdsaCrt, Base64Fingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8="},
-		{"Base64UrlFingerprint", args{ecdsaCrt, Base64UrlFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8="},
-		{"Base64RawFingerprint", args{ecdsaCrt, Base64RawFingerprint}, "OAEWIezcwhcukzoe8jF+/FNaFhwAMzruP4Sr+rTmQL8"},
-		{"Base64RawURLFingerprint", args{ecdsaCrt, Base64RawURLFingerprint}, "OAEWIezcwhcukzoe8jF-_FNaFhwAMzruP4Sr-rTmQL8"},
-		{"EmojiFingerprint", args{ecdsaCrt, EmojiFingerprint}, "💨🎱🌼📆🎠🎉🎿🚙🍪🌊♦️💡✌️🐮🔒❌🖕😬🌼👦👍👑♦️🇬🇧👂🔬📌♿🚀🚜🍆🐑"},
-		{"Unknown", args{ecdsaCrt, 100}, ""},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := EncodedFingerprint(tt.args.cert, tt.args.encoding); got != tt.want {
-				t.Errorf("EncodedFingerprint() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func Test_generateSubjectKeyID(t *testing.T) {
 	ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt")
 	rsaCrt := decodeCertificateFile(t, "testdata/smallstep.crt")