diff --git a/cmd/tk/check.go b/cmd/tk/check.go
index a74fee3eb13e95cc20ecb9200b122a858d377c5c..29cf999bce6eec531a5be77e7a5aad9e6323d9ce 100644
--- a/cmd/tk/check.go
+++ b/cmd/tk/check.go
@@ -45,9 +45,6 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
 
 	logAction("checking prerequisites")
 	checkFailed := false
-	if !sshCheck() {
-		checkFailed = true
-	}
 
 	if !kubectlCheck(ctx, ">=1.18.0") {
 		checkFailed = true
@@ -76,21 +73,6 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-func sshCheck() bool {
-	ok := true
-	for _, cmd := range []string{"ssh-keygen", "ssh-keyscan"} {
-		_, err := exec.LookPath(cmd)
-		if err != nil {
-			logFailure("%s not found", cmd)
-			ok = false
-		} else {
-			logSuccess("%s found", cmd)
-		}
-	}
-
-	return ok
-}
-
 func kubectlCheck(ctx context.Context, version string) bool {
 	_, err := exec.LookPath("kubectl")
 	if err != nil {
diff --git a/cmd/tk/create_source_git.go b/cmd/tk/create_source_git.go
index b5953b2798d31679d570f4f32e6fbc8526f0ecd1..97f4934fbed3521a60a37a25d1ce76fd73cb9abb 100644
--- a/cmd/tk/create_source_git.go
+++ b/cmd/tk/create_source_git.go
@@ -2,20 +2,24 @@ package main
 
 import (
 	"context"
+	"crypto/elliptic"
 	"fmt"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"time"
+
 	sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
-	"io/ioutil"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
-	"net/url"
-	"os"
 	"sigs.k8s.io/controller-runtime/pkg/client"
-	"strings"
+
+	"github.com/fluxcd/toolkit/pkg/ssh"
 )
 
 var createSourceGitCmd = &cobra.Command{
@@ -40,11 +44,19 @@ For private Git repositories, the basic authentication credentials are stored in
     --url=https://github.com/stefanprodan/podinfo \
     --tag-semver=">=3.2.0 <3.3.0"
 
-  #  Create a source from a Git repository using SSH authentication
+  # Create a source from a Git repository using SSH authentication
   create source git podinfo \
     --url=ssh://git@github.com/stefanprodan/podinfo \
     --branch=master
 
+  # Create a source from a Git repository using SSH authentication and an
+  # ECDSA P-521 curve public key
+  create source git podinfo \
+    --url=ssh://git@github.com/stefanprodan/podinfo \
+    --branch=master \
+    --ssh-key-algorithm=ecdsa \
+    --ssh-ecdsa-curve=p521
+
   # Create a source from a Git repository using basic authentication
   create source git podinfo \
     --url=https://github.com/stefanprodan/podinfo \
@@ -55,12 +67,15 @@ For private Git repositories, the basic authentication credentials are stored in
 }
 
 var (
-	sourceGitURL      string
-	sourceGitBranch   string
-	sourceGitTag      string
-	sourceGitSemver   string
-	sourceGitUsername string
-	sourceGitPassword string
+	sourceGitURL          string
+	sourceGitBranch       string
+	sourceGitTag          string
+	sourceGitSemver       string
+	sourceGitUsername     string
+	sourceGitPassword     string
+	sourceGitKeyAlgorithm PublicKeyAlgorithm = "rsa"
+	sourceGitRSABits      RSAKeyBits         = 2048
+	sourceGitECDSACurve                      = ECDSACurve{elliptic.P384()}
 )
 
 func init() {
@@ -70,6 +85,9 @@ func init() {
 	createSourceGitCmd.Flags().StringVar(&sourceGitSemver, "tag-semver", "", "git tag semver range")
 	createSourceGitCmd.Flags().StringVarP(&sourceGitUsername, "username", "u", "", "basic authentication username")
 	createSourceGitCmd.Flags().StringVarP(&sourceGitPassword, "password", "p", "", "basic authentication password")
+	createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
+	createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
+	createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
 
 	createSourceCmd.AddCommand(createSourceGitCmd)
 }
@@ -98,14 +116,66 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
 	withAuth := false
-	if strings.HasPrefix(sourceGitURL, "ssh") {
-		if err := generateSSH(ctx, name, u.Host, tmpDir); err != nil {
+	// TODO(hidde): move all auth prep to separate func?
+	if u.Scheme == "ssh" {
+		logAction("generating deploy key pair")
+		pair, err := generateKeyPair(ctx)
+		if err != nil {
+			return err
+		}
+
+		fmt.Printf("%s", pair.PublicKey)
+		prompt := promptui.Prompt{
+			Label:     "Have you added the deploy key to your repository",
+			IsConfirm: true,
+		}
+		if _, err := prompt.Run(); err != nil {
+			return fmt.Errorf("aborting")
+		}
+
+		logAction("collecting preferred public key from SSH server")
+		hostKey, err := scanHostKey(ctx, u)
+		if err != nil {
+			return err
+		}
+		logSuccess("collected public key from SSH server:")
+		fmt.Printf("%s", hostKey)
+
+		logAction("applying secret with keys")
+		secret := corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      name,
+				Namespace: namespace,
+			},
+			StringData: map[string]string{
+				"identity":     string(pair.PrivateKey),
+				"identity.pub": string(pair.PublicKey),
+				"known_hosts":  string(hostKey),
+			},
+		}
+		if err := upsertSecret(ctx, kubeClient, secret); err != nil {
 			return err
 		}
 		withAuth = true
 	} else if sourceGitUsername != "" && sourceGitPassword != "" {
-		if err := generateBasicAuth(ctx, name); err != nil {
+		logAction("applying secret with basic auth credentials")
+		secret := corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      name,
+				Namespace: namespace,
+			},
+			StringData: map[string]string{
+				"username": sourceGitUsername,
+				"password": sourceGitPassword,
+			},
+		}
+		if err := upsertSecret(ctx, kubeClient, secret); err != nil {
 			return err
 		}
 		withAuth = true
@@ -145,11 +215,6 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 		gitRepository.Spec.Reference.Branch = sourceGitBranch
 	}
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
-	if err != nil {
-		return err
-	}
-
 	logAction("applying source")
 	if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
 		return err
@@ -181,55 +246,59 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-func generateBasicAuth(ctx context.Context, name string) error {
-	logAction("saving credentials")
-	credentials := fmt.Sprintf("--from-literal=username='%s' --from-literal=password='%s'",
-		sourceGitUsername, sourceGitPassword)
-	secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-",
-		namespace, name, credentials)
-	if _, err := utils.execCommand(ctx, ModeOS, secret); err != nil {
-		return fmt.Errorf("kubectl create secret failed")
+func generateKeyPair(ctx context.Context) (*ssh.KeyPair, error) {
+	var keyGen ssh.KeyPairGenerator
+	switch sourceGitKeyAlgorithm.String() {
+	case "rsa":
+		keyGen = ssh.NewRSAGenerator(int(sourceGitRSABits))
+	case "ecdsa":
+		keyGen = ssh.NewECDSAGenerator(sourceGitECDSACurve.Curve)
+	case "ed25519":
+		keyGen = ssh.NewEd25519Generator()
+	default:
+		return nil, fmt.Errorf("unsupported public key algorithm '%s'", sourceGitKeyAlgorithm.String())
 	}
-	return nil
+	pair, err := keyGen.Generate()
+	if err != nil {
+		return nil, fmt.Errorf("key pair generation failed: %w", err)
+	}
+	return pair, nil
 }
 
-func generateSSH(ctx context.Context, name, host, tmpDir string) error {
-	logGenerate("generating host key for %s", host)
-
-	command := fmt.Sprintf("ssh-keyscan %s > %s/known_hosts", host, tmpDir)
-	if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
-		return fmt.Errorf("ssh-keyscan failed")
+func scanHostKey(ctx context.Context, url *url.URL) ([]byte, error) {
+	host := url.Host
+	if url.Port() == "" {
+		host = host + ":22"
 	}
-
-	logGenerate("generating deploy key")
-
-	command = fmt.Sprintf("ssh-keygen -b 2048 -t rsa -f %s/identity -q -N \"\"", tmpDir)
-	if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
-		return fmt.Errorf("ssh-keygen failed")
+	hostKey, err := ssh.ScanHostKey(host, 30*time.Second)
+	if err != nil {
+		return nil, fmt.Errorf("SSH key scan for host '%s' failed: %w", host, err)
 	}
+	return hostKey, nil
+}
 
-	command = fmt.Sprintf("cat %s/identity.pub", tmpDir)
-	if deployKey, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
-		return fmt.Errorf("unable to read identity.pub: %w", err)
-	} else {
-		fmt.Print(deployKey)
+func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
+	namespacedName := types.NamespacedName{
+		Namespace: secret.GetNamespace(),
+		Name:      secret.GetName(),
 	}
 
-	prompt := promptui.Prompt{
-		Label:     "Have you added the deploy key to your repository",
-		IsConfirm: true,
-	}
-	if _, err := prompt.Run(); err != nil {
-		return fmt.Errorf("aborting")
+	var existing corev1.Secret
+	err := kubeClient.Get(ctx, namespacedName, &existing)
+	if err != nil {
+		if errors.IsNotFound(err) {
+			if err := kubeClient.Create(ctx, &existing); err != nil {
+				return err
+			} else {
+				return nil
+			}
+		}
+		return err
 	}
 
-	logAction("saving keys")
-	files := fmt.Sprintf("--from-file=%s/identity --from-file=%s/identity.pub --from-file=%s/known_hosts",
-		tmpDir, tmpDir, tmpDir)
-	secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-",
-		namespace, name, files)
-	if _, err := utils.execCommand(ctx, ModeOS, secret); err != nil {
-		return fmt.Errorf("create secret failed")
+	existing.StringData = secret.StringData
+	if err := kubeClient.Update(ctx, &existing); err != nil {
+		return err
 	}
 	return nil
 }
diff --git a/cmd/tk/flags.go b/cmd/tk/flags.go
new file mode 100644
index 0000000000000000000000000000000000000000..2ae65b9e24f159f864cb426ae2ac43dd02f3cd6a
--- /dev/null
+++ b/cmd/tk/flags.go
@@ -0,0 +1,112 @@
+package main
+
+import (
+	"crypto/elliptic"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
+
+type PublicKeyAlgorithm string
+
+func (a *PublicKeyAlgorithm) String() string {
+	return string(*a)
+}
+
+func (a *PublicKeyAlgorithm) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no public key algorithm given, must be one of: %s",
+			strings.Join(supportedPublicKeyAlgorithms, ", "))
+	}
+	for _, v := range supportedPublicKeyAlgorithms {
+		if str == v {
+			*a = PublicKeyAlgorithm(str)
+			return nil
+		}
+	}
+	return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
+		str, strings.Join(supportedPublicKeyAlgorithms, ", "))
+}
+
+func (a *PublicKeyAlgorithm) Type() string {
+	return "publicKeyAlgorithm"
+}
+
+func (a *PublicKeyAlgorithm) Description() string {
+	return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
+}
+
+var defaultRSAKeyBits = 2048
+
+type RSAKeyBits int
+
+func (b *RSAKeyBits) String() string {
+	return strconv.Itoa(int(*b))
+}
+
+func (b *RSAKeyBits) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		*b = RSAKeyBits(defaultRSAKeyBits)
+		return nil
+	}
+	bits, err := strconv.Atoi(str)
+	if err != nil {
+		return err
+	}
+	if bits%8 != 0 {
+		return fmt.Errorf("RSA key bit size should be a multiples of 8")
+	}
+	*b = RSAKeyBits(bits)
+	return nil
+}
+
+func (b *RSAKeyBits) Type() string {
+	return "rsaKeyBits"
+}
+
+func (b *RSAKeyBits) Description() string {
+	return "SSH RSA public key bit size (multiplies of 8)"
+}
+
+type ECDSACurve struct {
+	elliptic.Curve
+}
+
+var supportedECDSACurves = map[string]elliptic.Curve{
+	"p256": elliptic.P256(),
+	"p384": elliptic.P384(),
+	"p521": elliptic.P521(),
+}
+
+func (c *ECDSACurve) String() string {
+	if c.Curve == nil {
+		return ""
+	}
+	return strings.ToLower(strings.Replace(c.Curve.Params().Name, "-", "", 1))
+}
+
+func (c *ECDSACurve) Set(str string) error {
+	if v, ok := supportedECDSACurves[str]; ok {
+		*c = ECDSACurve{v}
+		return nil
+	}
+	return fmt.Errorf("unsupported curve '%s', should be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
+}
+
+func (c *ECDSACurve) Type() string {
+	return "ecdsaCurve"
+}
+
+func (c *ECDSACurve) Description() string {
+	return fmt.Sprintf("SSH ECDSA public key curve (%s)", strings.Join(ecdsaCurves(), ", "))
+}
+
+func ecdsaCurves() []string {
+	keys := make([]string, 0, len(supportedECDSACurves))
+	for k := range supportedECDSACurves {
+		keys = append(keys, k)
+	}
+	return keys
+}
diff --git a/go.mod b/go.mod
index 8f051ccbfc724aa4af2ebfb8fd926e0403928040..1d0ca0ccda827de958ebe7b5f8e028523aeae96f 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
 	github.com/fluxcd/source-controller v0.0.1-beta.1
 	github.com/manifoldco/promptui v0.7.0
 	github.com/spf13/cobra v1.0.0
+	golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
 	k8s.io/api v0.18.2
 	k8s.io/apimachinery v0.18.2
 	k8s.io/client-go v0.18.2
diff --git a/pkg/ssh/host_key.go b/pkg/ssh/host_key.go
new file mode 100644
index 0000000000000000000000000000000000000000..a02bcaabc4e09e30a5a7ed9d8eacd6e71532d1ca
--- /dev/null
+++ b/pkg/ssh/host_key.go
@@ -0,0 +1,55 @@
+package ssh
+
+import (
+	"encoding/base64"
+	"fmt"
+	"net"
+	"time"
+
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/crypto/ssh/knownhosts"
+)
+
+// ScanHostKey collects the given host's preferred public key for the
+// Any errors (e.g. authentication  failures) are ignored, except if
+// no key could be collected from the host.
+func ScanHostKey(host string, timeout time.Duration) ([]byte, error) {
+	col := &HostKeyCollector{}
+	config := &ssh.ClientConfig{
+		HostKeyCallback: col.StoreKey(),
+		Timeout:         timeout,
+	}
+	client, err := ssh.Dial("tcp", host, config)
+	if err == nil {
+		defer client.Close()
+	}
+	if len(col.knownKeys) > 0 {
+		return col.knownKeys, nil
+	}
+	return col.knownKeys, err
+}
+
+// HostKeyCollector offers a StoreKey method which provides an
+// HostKeyCallBack to collect public keys from an SSH server.
+type HostKeyCollector struct {
+	knownKeys []byte
+}
+
+// StoreKey stores the public key in bytes as returned by the host.
+// To collect multiple public key types from the host, multiple
+// SSH dials need with the ClientConfig HostKeyAlgorithms set to
+// the algorithm you want to collect.
+func (c *HostKeyCollector) StoreKey() ssh.HostKeyCallback {
+	return func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+		c.knownKeys = append(
+			c.knownKeys,
+			fmt.Sprintf("%s %s %s\n", knownhosts.Normalize(hostname), key.Type(), base64.StdEncoding.EncodeToString(key.Marshal()))...,
+		)
+		return nil
+	}
+}
+
+// GetKnownKeys returns the collected public keys in bytes.
+func (c *HostKeyCollector) GetKnownKeys() []byte {
+	return c.knownKeys
+}
diff --git a/pkg/ssh/key_pair.go b/pkg/ssh/key_pair.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f8fcd40ee7e1c1feac44d2da72977294a968004
--- /dev/null
+++ b/pkg/ssh/key_pair.go
@@ -0,0 +1,130 @@
+package ssh
+
+import (
+	"crypto/ecdsa"
+	"crypto/ed25519"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+
+	"golang.org/x/crypto/ssh"
+)
+
+// KeyPair holds the public and private key PEM block bytes.
+type KeyPair struct {
+	PublicKey  []byte
+	PrivateKey []byte
+}
+
+type KeyPairGenerator interface {
+	Generate() (*KeyPair, error)
+}
+
+type RSAGenerator struct {
+	bits int
+}
+
+func NewRSAGenerator(bits int) KeyPairGenerator {
+	return &RSAGenerator{bits}
+}
+
+func (g *RSAGenerator) Generate() (*KeyPair, error) {
+	pk, err := rsa.GenerateKey(rand.Reader, g.bits)
+	if err != nil {
+		return nil, err
+	}
+	err = pk.Validate()
+	if err != nil {
+		return nil, err
+	}
+	pub, err := generatePublicKey(&pk.PublicKey)
+	if err != nil {
+		return nil, err
+	}
+	priv, err := encodePrivateKeyToPEM(pk)
+	if err != nil {
+		return nil, err
+	}
+	return &KeyPair{
+		PublicKey:  pub,
+		PrivateKey: priv,
+	}, nil
+}
+
+type ECDSAGenerator struct {
+	c elliptic.Curve
+}
+
+func NewECDSAGenerator(c elliptic.Curve) KeyPairGenerator {
+	return &ECDSAGenerator{c}
+}
+
+func (g *ECDSAGenerator) Generate() (*KeyPair, error) {
+	pk, err := ecdsa.GenerateKey(g.c, rand.Reader)
+	if err != nil {
+		return nil, err
+	}
+	pub, err := generatePublicKey(&pk.PublicKey)
+	if err != nil {
+		return nil, err
+	}
+	priv, err := encodePrivateKeyToPEM(pk)
+	if err != nil {
+		return nil, err
+	}
+	return &KeyPair{
+		PublicKey:  pub,
+		PrivateKey: priv,
+	}, nil
+}
+
+type Ed25519Generator struct{}
+
+func NewEd25519Generator() KeyPairGenerator {
+	return &Ed25519Generator{}
+}
+
+func (g *Ed25519Generator) Generate() (*KeyPair, error) {
+	pk, pv, err := ed25519.GenerateKey(rand.Reader)
+	if err != nil {
+		return nil, err
+	}
+	pub, err := generatePublicKey(pk)
+	if err != nil {
+		return nil, err
+	}
+	priv, err := encodePrivateKeyToPEM(pv)
+	if err != nil {
+		return nil, err
+	}
+	return &KeyPair{
+		PublicKey:  pub,
+		PrivateKey: priv,
+	}, nil
+}
+
+func generatePublicKey(pk interface{}) ([]byte, error) {
+	b, err := ssh.NewPublicKey(pk)
+	if err != nil {
+		return nil, err
+	}
+	k := ssh.MarshalAuthorizedKey(b)
+	return k, nil
+}
+
+// encodePrivateKeyToPEM encodes the given private key to a PEM block.
+// The encoded format is PKCS#8 for universal support of the most
+// common key types (rsa, ecdsa, ed25519).
+func encodePrivateKeyToPEM(pk interface{}) ([]byte, error) {
+	b, err := x509.MarshalPKCS8PrivateKey(pk)
+	if err != nil {
+		return nil, err
+	}
+	block := pem.Block{
+		Type:  "PRIVATE KEY",
+		Bytes: b,
+	}
+	return pem.EncodeToMemory(&block), nil
+}