将 pem 键转换为 ssh-rsa 格式

我有一个 der格式的证书,用这个命令从中生成一个公钥:

openssl x509 -inform der -in ejbcacert.cer -noout -pubkey > pub1key.pub

结果就是:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7vbqajDw4o6gJy8UtmIbkcpnk
O3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2
eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1
QWPdspTBKcxeFbccDwIDAQAB
-----END PUBLIC KEY-----

我如何获得这样的公钥? 从证书或 从这个公钥?

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7vbqajDw4o6gJy8UtmIbkcpnkO3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1QWPdspTBKcxeFbccDw==

这是通过以下命令获得的:

ssh-keygen -y -f private_key1.pem > public_key1.pub
269345 次浏览

为了回答我自己的问题,在 openssl 邮件列表上发帖后得到了这个:

下面是将 OpenSSL 公钥转换为 OpenSSH 公钥的 C 代码。 你可以从 这个链接中获取代码,然后自己编译:

static unsigned char pSshHeader[11] = { 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2D, 0x72, 0x73, 0x61};


static int SshEncodeBuffer(unsigned char *pEncoding, int bufferLen, unsigned char* pBuffer)
{
int adjustedLen = bufferLen, index;
if (*pBuffer & 0x80)
{
adjustedLen++;
pEncoding[4] = 0;
index = 5;
}
else
{
index = 4;
}
pEncoding[0] = (unsigned char) (adjustedLen >> 24);
pEncoding[1] = (unsigned char) (adjustedLen >> 16);
pEncoding[2] = (unsigned char) (adjustedLen >>  8);
pEncoding[3] = (unsigned char) (adjustedLen      );
memcpy(&pEncoding[index], pBuffer, bufferLen);
return index + bufferLen;
}


int main(int argc, char**  argv)
{
int iRet = 0;
int nLen = 0, eLen = 0;
int encodingLength = 0;
int index = 0;
unsigned char *nBytes = NULL, *eBytes = NULL;
unsigned char* pEncoding = NULL;
FILE* pFile = NULL;
EVP_PKEY *pPubKey = NULL;
RSA* pRsa = NULL;
BIO *bio, *b64;


ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();


if (argc != 3)
{
printf("usage: %s public_key_file_name ssh_key_description\n", argv[0]);
iRet = 1;
goto error;
}


pFile = fopen(argv[1], "rt");
if (!pFile)
{
printf("Failed to open the given file\n");
iRet = 2;
goto error;
}


pPubKey = PEM_read_PUBKEY(pFile, NULL, NULL, NULL);
if (!pPubKey)
{
printf("Unable to decode public key from the given file: %s\n", ERR_error_string(ERR_get_error(), NULL));
iRet = 3;
goto error;
}


if (EVP_PKEY_type(pPubKey->type) != EVP_PKEY_RSA)
{
printf("Only RSA public keys are currently supported\n");
iRet = 4;
goto error;
}


pRsa = EVP_PKEY_get1_RSA(pPubKey);
if (!pRsa)
{
printf("Failed to get RSA public key : %s\n", ERR_error_string(ERR_get_error(), NULL));
iRet = 5;
goto error;
}


// reading the modulus
nLen = BN_num_bytes(pRsa->n);
nBytes = (unsigned char*) malloc(nLen);
BN_bn2bin(pRsa->n, nBytes);


// reading the public exponent
eLen = BN_num_bytes(pRsa->e);
eBytes = (unsigned char*) malloc(eLen);
BN_bn2bin(pRsa->e, eBytes);


encodingLength = 11 + 4 + eLen + 4 + nLen;
// correct depending on the MSB of e and N
if (eBytes[0] & 0x80)
encodingLength++;
if (nBytes[0] & 0x80)
encodingLength++;


pEncoding = (unsigned char*) malloc(encodingLength);
memcpy(pEncoding, pSshHeader, 11);


index = SshEncodeBuffer(&pEncoding[11], eLen, eBytes);
index = SshEncodeBuffer(&pEncoding[11 + index], nLen, nBytes);


b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_new_fp(stdout, BIO_NOCLOSE);
BIO_printf(bio, "ssh-rsa ");
bio = BIO_push(b64, bio);
BIO_write(bio, pEncoding, encodingLength);
BIO_flush(bio);
bio = BIO_pop(b64);
BIO_printf(bio, " %s\n", argv[2]);
BIO_flush(bio);
BIO_free_all(bio);
BIO_free(b64);


error:
if (pFile)
fclose(pFile);
if (pRsa)
RSA_free(pRsa);
if (pPubKey)
EVP_PKEY_free(pPubKey);
if (nBytes)
free(nBytes);
if (eBytes)
free(eBytes);
if (pEncoding)
free(pEncoding);


EVP_cleanup();
ERR_free_strings();
return iRet;
}

不需要编译东西,你可以用 ssh-keygen做同样的事情:

ssh-keygen -f pub1key.pub -i

将以 openssl 格式从 pub1key.pub读取公钥并以 OpenSSH 格式输出。

注意 : 在某些情况下,您需要指定输入格式:

ssh-keygen -f pub1key.pub -i -mPKCS8

来自 ssh-keygen docs (来自 man ssh-keygen) :

- m Key _ format为-i (import)或-e (export)转换选项指定键格式。支持的密钥格式有: “ RFC4716”(RFC4716/SSH2公钥或私钥)、“ PKCS8”(PEM PKCS8公钥)或“ PEM”(PEM 公钥)。默认的转换格式是“ RFC4716”。

我和

Ssh-keygen-i-f $sshkeysfile > > authored_ keys

功劳归这里

下面的脚本将获得 base64编码的 DER 格式的 ci.jenkins-ci.org 公钥证书,并将其转换为 OpenSSH 公钥文件。这段代码假设使用了2048位 RSA 密钥,并从 Ian Boyd 的 回答中获得了很多信息。我已经在 Jenkins wiki 的 这篇文章评论中对它的工作原理做了更多的解释。

echo -n "ssh-rsa " > jenkins.pub
curl -sfI https://ci.jenkins-ci.org/ | grep -i X-Instance-Identity | tr -d \\r | cut -d\  -f2 | base64 -d | dd bs=1 skip=32 count=257 status=none | xxd -p -c257 | sed s/^/00000007\ 7373682d727361\ 00000003\ 010001\ 00000101\ / | xxd -p -r | base64 -w0 >> jenkins.pub
echo >> jenkins.pub

不需要脚本或其他“技巧”: opensslssh-keygen就足够了。我假设没有密码的钥匙(这是坏的)。

生成一个 RSA 对

以下所有方法都以相同的格式提供一个 RSA 密钥对

  1. 与 openssl (Man Genrsa)

    openssl genrsa -out dummy-genrsa.pem 2048
    

    在 OpenSSL v1.0.1 genrsa 被取代了 by genpkey中,这是一种新的实现方法(人类 Genpkey) :

    openssl genpkey -algorithm RSA -out dummy-genpkey.pem -pkeyopt rsa_keygen_bits:2048
    
  2. With ssh-keygen

    ssh-keygen -t rsa -b 2048 -f dummy-ssh-keygen.pem -N '' -C "Test Key"
    

Converting DER to PEM

If you have an RSA key pair in DER format, you may want to convert it to PEM to allow the format conversion below:

Generation:

openssl genpkey -algorithm RSA -out genpkey-dummy.cer -outform DER -pkeyopt rsa_keygen_bits:2048

转换:

openssl rsa -inform DER -outform PEM -in genpkey-dummy.cer -out dummy-der2pem.pem

从 PEM 格式的 RSA 对中提取公钥

  1. PEM 格式:

    openssl rsa -in dummy-xxx.pem -pubout
    
  2. in OpenSSH v2 format see:

    ssh-keygen -y -f dummy-xxx.pem
    

Notes

OS and software version:

[user@test1 ~]# cat /etc/redhat-release ; uname -a ; openssl version
CentOS release 6.5 (Final)
Linux test1.example.local 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
OpenSSL 1.0.1e-fips 11 Feb 2013

参考文献:

ssh-keygen -f private.pem -y > public.pub
ssh-keygen -i -m PKCS8 -f public-key.pem

FWIW,这个 BASH 脚本将采用 PEM 或 DER 格式的 X.509证书或 OpenSSL 公钥文件(也是 PEM 格式)作为第一个参数,并提供一个 OpenSSH RSA 公钥。这扩展了上面@mkalkov 的回答。需求包括 catgreptrddxxdsedxargsfileuuidgenbase64grep0(1.0 +) ,当然还有 grep1。除了 grep0(包含 base64)之外,所有这些几乎都可以保证是任何现代 Linux 系统基础安装的一部分,除了 xxd(Fedora 在 grep5包中显示)。如果有人想把它打扫干净,让它变得更好,那就告诫一下莱克特。

#!/bin/bash
#
# Extract a valid SSH format public key from an X509 public certificate.
#


# Variables:
pubFile=$1
fileType="no"
pkEightTypeFile="$pubFile"
tmpFile="/tmp/`uuidgen`-pkEightTypeFile.pk8"


# See if a file was passed:
[ ! -f "$pubFile" ] && echo "Error, bad or no input file $pubFile." && exit 1


# If it is a PEM format X.509 public cert, set $fileType appropriately:
pemCertType="X$(file $pubFile | grep 'PEM certificate')"
[ "$pemCertType" != "X" ] && fileType="PEM"


# If it is an OpenSSL PEM-format PKCS#8-style public key, set $fileType appropriately:
pkEightType="X$(grep -e '-BEGIN PUBLIC KEY-' $pubFile)"
[ "$pkEightType" != "X" ] && fileType="PKCS"


# If this is a file we can't recognise, try to decode a (binary) DER-format X.509 cert:
if [ "$fileType" = "no" ]; then
openssl x509 -in $pubFile -inform DER -noout
derResult=$(echo $?)
[ "$derResult" = "0" ] && fileType="DER"
fi


# Exit if not detected as a file we can use:
[ "$fileType" = "no" ] && echo "Error, input file not of type X.509 public certificate or OpenSSL PKCS#8-style public key (not encrypted)." && exit 1


# Convert the X.509 public cert to an OpenSSL PEM-format PKCS#8-style public key:
if [ "$fileType" = "PEM" -o "$fileType" = "DER" ]; then
openssl x509 -in $pubFile -inform $fileType -noout -pubkey > $tmpFile
pkEightTypeFile="$tmpFile"
fi


# Build the string:
# Front matter:
frontString="$(echo -en 'ssh-rsa ')"


# Encoded modulus and exponent, with appropriate pointers:
encodedModulus="$(cat $pkEightTypeFile | grep -v -e "----" | tr -d '\n' | base64 -d | dd bs=1 skip=32 count=257 status=none | xxd -p -c257 | sed s/^/00000007\ 7373682d727361\ 00000003\ 010001\ 00000101\ / | xxd -p -r | base64 -w0 )"


# Add a comment string based on the filename, just to be nice:
commentString=" $(echo $pubFile | xargs basename | sed -e 's/\.crt\|\.cer\|\.pem\|\.pk8\|\.der//')"


# Give the user a string:
echo $frontString $encodedModulus $commentString


# cleanup:
rm -f $tmpFile