前言

有一个SSH密钥跟了我很多年,更换电脑也不曾更换它。它不需要额外输入密码就能用,我将它的公钥用在了诸多平台。虽然很方便,但是安全性差(指没有密码)和匿名性差(指同一公钥在不同平台可能被关联)。

于是,计划在保留已有SSH密钥的情况下,先新创建一个SSH密钥专门用于GitHub,并加上密码增加安全性,后续再逐步为其他平台创建。在实际操作过程中,遇到了一些问题并进行研究,在此记录一番,希望对你有所帮助。

注意,以下内容均是以GitHub使用SSH连接为例,分别讨论单个或多个SSH密钥的使用等问题。可能存在不完善或少许错误,欢迎留言评论补充或指正。

开发环境

  • MacOS: 14.3.1
  • SSH: OpenSSH_9.4p1

单个SSH密钥

如果不考虑匿名性,每台设备只维护单个SSH密钥用于各个平台是很轻松的,加上密码安全性也有一定的保障。GitHub使用SSH进行连接的步骤如下:

1.1. 生成SSH密钥

  1. 执行密钥创建命令
# 这是使用RSA算法生成
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 现在一般推荐使用安全性更高的Ed25519算法生成
ssh-keygen -t ed25519 -C "your_email@example.com"

your_email@example.com一般是GitHub上账号的电子邮件地址(来自GitHub官方文档的说法)。不过,这有个问题,一台设备上如果只管理一个SSH密钥,然后不同平台上的电子邮件地址又不一样,那该填什么?

先不管GitHub的说法,我们可以通过使用手册命令看看-C选项的含义:

man ssh-keygen

输出很长,这里只展示关于-C-c选项的内容(补充:-t选项是指定密钥类型):

...
-C comment
        Provides a new comment.
        
-c      Requests changing the comment in the private and public key
        files.  The program will prompt for the file containing the
        private keys, for the passphrase if the key has one, and for the
        new comment.
...

可见-C选项后面跟的只是注释,方便你标识这个密钥(标识用途或所有者等)。实测,直接填your_email@example.com也一切正常!而且这个注释是可以通过-c选项修改的,修改命令参考如下:

ssh-keygen -c -f ~/.ssh/id_ed25519

实测能修改成功,并且不需要更新已经添加到GitHub的公钥,注释信息并不会影响身份验证。所以,填什么请随意。

  1. 设置密钥存放路径
# RSA算法生成
Enter file in which to save the key (/Users/xxx/.ssh/id_rsa):
# Ed25519算法生成
Enter file in which to save the key (/Users/xxx/.ssh/id_ed25519):

建议直接按Enter键使用它已经给出的默认文件路径,具体原因请看后面无密码密钥部分的内容。

  1. 设置密钥密码
Enter passphrase (empty for no passphrase):

如果不使用密码可以直接按Enter键,不过建议使用密码。生成密钥成功后,可以参考以下命令新增/修改/删除密码:

ssh-keygen -p -f ~/.ssh/id_ed25519
  1. 生成密钥成功
Your identification has been saved in /Users/xxx/.ssh/id_rsa(或id_ed25519)
Your public key has been saved in /Users/xxx/.ssh/id_rsa.pub(或id_ed25519.pub)
The key fingerprint is: xxx your_email@example.com
The key's randomart image is: xxx

参考文档:

1.2. 添加SSH密钥

打开SSH and GPG keys页面,如果没有登录请先登录。点击[New SSH key]按钮添加SSH密钥:

screenshot1

  • Title:给密钥设置描述性标签,可以用于区分设备。当某个设备的私钥泄漏时,方便快速找到该设备的SSH密钥并删除
  • Key type:密钥类型,分为Authentication Key(身份验证)和Signing Key(提交签名),使用默认的身份验证类型即可
  • Key:前面生成的公钥,可以通过以下命令将公钥复制到剪贴板,也可以找到公钥文件(.pub扩展名)用文本程序打开复制
# 复制RSA算法生成的密钥
pbcopy < ~/.ssh/id_rsa.pub
# 复制Ed25519算法生成的密钥
pbcopy < ~/.ssh/id_ed25519.pub

由于添加密钥是敏感操作,GitHub一般会让你再次验证身份,参考Sudo 模式

参考文档:

1.3. 测试SSH连接

通过执行以下命令测试:

ssh -T git@github.com

一般会得到这样的输出:

The authenticity of host 'github.com (20.205.243.166)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

其中SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU是服务器的公钥指纹,用于验证服务器的身份。GitHub的SSH密钥指纹

SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s (RSA)
SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM (ECDSA)
SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU (Ed25519)

确认公钥匹配后,输入yes完成身份验证,会自动在~/.ssh/known_hosts文件中保存已验证服务器信息,后续无需再次验证,除非服务器信息有变化。

如果密钥有密码,需要输入密码完成连接测试。

参考文档:

2.1. 简化密钥密码输入

如果你的SSH密钥有设置密码,那么你会发现每次使用(例如执行git pull命令)都需要输入一遍密码,相当麻烦。那么有什么办法可以解决这个问题呢?

这就不得不提到SSH代理,将密钥添加到代理,只需要输一次密码,后续无需重复输入密码。先在~/.ssh路径下创建config文件(如果该文件不存在):

touch ~/.ssh/config

然后往里面添加配置:

Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519
  • AddKeysToAgent:将密钥加入代理
  • UseKeychain:将密码存储在钥匙串
  • IdentityFile:指定密钥文件路径

配置后,只需要输入一次密码,密码会自动保存到钥匙串,打开钥匙串应用搜索SSH关键词可以找到存储记录。通过以下命令可以查看已经添加到SSH代理中的密钥:

ssh-add -l

当重启电脑后(也可以通过killall ssh-agent命令终止代理,在需要时会自动重启)马上查看SSH代理中的密钥,你会发现没有加载任何密钥:

The agent has no identities.

这时使用SSH连接(例如执行git pull命令),会自动添加密钥,也无需再输入密码。

关于配置中的Host,这里引出另一个问题,现在只维护单个密钥用于多个平台,按前面的配置,只有连接GitHub时能简化密码输入,其他平台还是每次都需要输入密码。

这很好解决,在config文件中参考前面的配置修改Host给其他平台也增加配置即可。需要注意一点,有些平台的Host可能类似这样的git.github.com,这时你要么完全按照这个配置,要么使用通配符*.github.com

如果平台多了,配置也挺麻烦的,本来只维护单个密钥就为了省事,那还有没有更省事的方法呢?肯定是有的,参考如下配置:

Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

参考文档:

多个SSH密钥

1.1. 生成/添加/测试SSH密钥

参考前面单个SSH密钥的生成/添加/测试,除了设置密钥存放路径时需要重新命名区分已有的密钥文件外,操作基本一样。

我一般按照这个格式id_aaa.bbb命名,aaa是密钥类型(例如ed25519),bbb是平台标识(例如github)。按自己喜好命名,方便自己标识就行。

2.1. 简化密钥密码输入

参考前面单个SSH密钥的简化密码输入,可以针对不同平台配置不同的密钥文件:

Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519.github
  
Host xxx.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_rsa.xxx

或者更简单点(不过都维护多个密钥了,应该很少这么做了吧):

Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519
  
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_rsa

如果按照上方示例中的配置,会根据先后顺序匹配。例如id_ed25519密钥会被优先匹配,只有当匹配失败或密码错误,才会继续往下匹配。

无密码密钥

前面着重讲设有密码的密钥,那如果密钥不设置密码还需要配置吗?

这里先引出一个默认密钥文件的说法,默认密钥文件指使用默认名称命名并放在默认路径下的密钥。默认路径默认名称分别指:

  • 默认路径~/.ssh
  • 默认名称id_dsa | id_ecdsa | id_ecdsa_sk | id_ed25519 | id_ed25519_sk | id_rsa

进而引出:

  • 对于单个无密码密钥,只要属于默认密钥文件SSH会自动添加,无需再额外配置
  • 对于多个无密码密钥,不属于默认密钥文件的需要在config文件中配置,参考配置如下:
Host github.com
  IdentityFile ~/.ssh/id_ed25519.github

或许你会疑问,默认密钥文件的说法是怎么来的?

在已有密钥的情况下创建新密钥时,我发现自定义命名的密钥无法直接使用,同时将密钥名称改为默认名称后又可以了,所以合理猜测存在一些默认密钥文件会被SSH自动添加(无论密钥是否有密码,有密码的密钥不配置也能正常使用,只是需要输入密码)。

关于猜测的具体验证过程请看续篇:GitHub - 使用SSH进行连接(续)

补充内容

除了通过配置config文件简化密码输入,还可以通过以下方法。

~/.bash_profile~/.zshrc文件末尾加上:

if ! ssh-add -l | grep -q "your_email@example.com"; then
    ssh-add --apple-use-keychain ~/.ssh/id_ed25519.github
fi

其中your_email@example.com是创建密钥时的注释(参考前面的密钥创建),~/.ssh/id_ed25519.github是密钥文件路径,请根据实际情况替换。

追加内容保存后,重新打开终端或用source命令使其生效。根据提示,首次输入密码后,后续无需再输入密码。

这个原理很简单,每当打开终端时,首先通过ssh-add -l命令获取已经添加到SSH代理中的密钥信息,然后根据密钥信息搜索是否包含特定注释信息,以此判断密钥是否已经被添加到SSH代理中,最后如果没添加则通过ssh-add --apple-use-keychain命令添加。

--apple-use-keychain的作用是将首次输入的密码存储到钥匙串中。补充一点,以前是用-K,现在已经被弃用。

注意,这个方法有个缺点,如果重启电脑后你没先打开终端触发自动加载密钥,那就不会加载。

例如重启电脑后,直接用Sourcetree软件执行Git相关操作,会报错。不过,Android Studio是例外,猜测可能是因为它启动时也启动了shell进程,触发了密钥加载。

简单验证一下猜测:删除钥匙串中保存的密钥密码,并且通过killall ssh-agent命令终止代理(清除已添加的密钥),重新启动Android Studio,随意打开一个项目,结果卡住了一会儿,还弹了警告:

screenshot2

Learn more按钮指向Shell Environment Loading文章。

同时内置终端卡在密钥密码输入。结合Learn more按钮指向的文章可知,Android Studio果然启动了shell进程,当遇到首次加载密钥需要输入密码时,会因为等待密码输入而卡住,直到等待超时。

最后

如果这篇文章对你有所帮助,点赞👍收藏🌟支持一下吧,谢谢~


本篇文章由@crasowas发布于CSDN。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐