9
5

More than 5 years have passed since last update.

ECDSA鍵の復元テスト

Last updated at Posted at 2018-09-16

はじめに

この記事を書いている2018年9月現在、OpenSSHでは、鍵に使用する公開鍵暗号(電子署名)アルゴリズムとして、楕円曲線暗号であるECDSAを選択することができます。
そして、公開鍵暗号の常ですが、秘密鍵の中で本当に秘密にすべき情報というのは実はそれほど大きなデータではありません。

であれば、その核となるデータから鍵を復元できるのではないかと思い立ちテストしたため、その備忘録として記します。

なお、環境は Windows10 WSL/Ubuntu 16.04.5 LTS + OpenSSL 1.0.2g + OpenSSH 7.2p2、使用する楕円曲線は prime256v1 とします。

ECDSA鍵について

OpenSSHのECDSA鍵は、ssh-keygenコマンドで、-t ecdsaオプションを指定することで作成できます。

例えば次は、256bit ECDSA ( prime256v1 ) の鍵を、id_ecdsa(秘密鍵), id_ecdsa.pub(公開鍵) に作成・保存する例です。

$ ssh-keygen -t ecdsa -b 256 -P "" -C "" -f id_ecdsa
Generating public/private ecdsa key pair.
Your identification has been saved in id_ecdsa.
Your public key has been saved in id_ecdsa.pub.
The key fingerprint is:
SHA256:+EC2TlpJR9lFwBxdpVkN2PdhZtLAxeskwY+a/RZPXCM
The key's randomart image is:
+---[ECDSA 256]---+
|        .=o*==B++|
|       .. + o=.@o|
|      + .     @.+|
|     + =     E =o|
|      B S   + =.o|
|     = o   o . oo|
|    . . .     ..o|
|               o.|
|              .  |
+----[SHA256]-----+

特に形式を指定せず、またパスフレーズを空で指定すれば、秘密鍵は暗号化なしのPEM形式で保存されます。これでファイルサイズは227バイトです。

秘密鍵id_ecdsa
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3RvoAoGCCqGSM49
AwEHoUQDQgAE7W5UuuFIumTq9w7ufxOgrFOlvOUaaUD8EHlIb+nWTzgCtFBN560O
aWs+eOl0MH4PT80UeALizOJjyXUfeT+Upw==
-----END EC PRIVATE KEY-----

しかし、opensslコマンドで情報を見てみると、本当に秘密にすべき情報は(以下のコマンドの出力にある通り)256bit、base64エンコードした状態でも44バイトに過ぎないことが分かります。

鍵情報の参照
$ openssl ec -text -noout -in id_ecdsa
read EC key
Private-Key: (256 bit)
priv:
    2b:f6:7e:8d:ef:0b:79:4e:7a:e1:ad:47:32:f0:ad:
    d7:8e:2f:22:1b:67:bc:72:12:01:a4:3c:1e:2b:3b:
    74:6f
pub:
    04:ed:6e:54:ba:e1:48:ba:64:ea:f7:0e:ee:7f:13:
    a0:ac:53:a5:bc:e5:1a:69:40:fc:10:79:48:6f:e9:
    d6:4f:38:02:b4:50:4d:e7:ad:0e:69:6b:3e:78:e9:
    74:30:7e:0f:4f:cd:14:78:02:e2:cc:e2:63:c9:75:
    1f:79:3f:94:a7
ASN1 OID: prime256v1
NIST CURVE: P-256

実際に対応するのは秘密鍵の次の強調部分です。

-----BEGIN EC PRIVATE KEY-----
MHcCAQEE ICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3Rv oAoGCCqGSM49

次のように、base64デコードして16進ダンプを見てみると、丁度"priv"の部分の256bit(+先頭に余分な8bit)に対応していることが分かります。

$ base64 -d <<< ICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3Rv | od -tx1 -An
 20 2b f6 7e 8d ef 0b 79 4e 7a e1 ad 47 32 f0 ad
 d7 8e 2f 22 1b 67 bc 72 12 01 a4 3c 1e 2b 3b 74
 6f

ECDSA鍵の復元

ということで、今回、上記の44バイト部分をパラメータとして与えて、元のECDSA鍵を復元するテストプログラムecrecover-p256を作りました。ただし、使用する楕円曲線はprime256v1限定です。

実行すると次のように、標準出力に秘密鍵を出力します。これはちゃんと元の鍵と一致するものです。

鍵復元
$ ./ecrecover-p256 ICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3Rv
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3RvoAoGCCqGSM49
AwEHoUQDQgAE7W5UuuFIumTq9w7ufxOgrFOlvOUaaUD8EHlIb+nWTzgCtFBN560O
aWs+eOl0MH4PT80UeALizOJjyXUfeT+Upw==
-----END EC PRIVATE KEY-----
$ ./ecrecover-p256 ICv2fo3vC3lOeuGtRzLwrdeOLyIbZ7xyEgGkPB4rO3Rv | diff -s id_ecdsa -
Files id_ecdsa and - are identical

ということで、( 鍵に使う楕円曲線がprime256v1限定ではありますが ) 核となる秘密情報のみから、鍵本体を復元できることが分かりました。

ソースコード

以下、ecrecover-p256のソース(C99)です。

gcc -o ecrecover-p256 ecrecover-p256.c -lcryptoというコマンドでビルドできます。なお、OpenSSLの開発用パッケージ(Ubuntuならlibssl-dev)が必要です。

中身はやっつけなので、特に解説はしません。リソース解法もさぼってます。
※OpenSSL1.0.2だとヘッダーにPEM_write_ECPrivateKey関数が見当たらなかったため、自分でプロトタイプをでっちあげました。関数本体はライブラリにあるのでちゃんと使うことができます。

ecrecover-p256.c
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/ec.h>

extern int PEM_write_ECPrivateKey(FILE *,EC_KEY *,void*,unsigned char*,int,void*,void*);

void b64decode(char* b64str, unsigned char* buffer, size_t* length) {
  BIO *bio, *b64;
  bio=BIO_new_mem_buf(b64str,-1);
  b64=BIO_new(BIO_f_base64());
  bio=BIO_push(b64,bio);
  BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
  *length = BIO_read(bio, buffer, strlen(b64str));
  BIO_free_all(bio);
}

int main(int argc,char *argv[]) {
  if ( argc<2 ) {
    fprintf(stderr,"Usage: %s b64string\n",argv[0]);
    return 1;
  }

  size_t b64len=strlen(argv[1]),decodelen;
  char buffer[b64len];
  b64decode(argv[1],buffer,&decodelen);
  BIGNUM *priv=BN_bin2bn(&buffer[1],decodelen-1,NULL);
  EC_GROUP *curve=EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
  EC_POINT *pub=EC_POINT_new(curve);
  BN_CTX *ctx=BN_CTX_new();
  EC_POINT_mul(curve,pub,priv,NULL,NULL,ctx);
  EC_KEY *key=EC_KEY_new();
  EC_KEY_set_group(key,curve);
  EC_KEY_set_asn1_flag(key,OPENSSL_EC_NAMED_CURVE);
  EC_KEY_set_private_key(key,priv);
  EC_KEY_set_public_key(key,pub);
  PEM_write_ECPrivateKey(stdout,key,NULL,NULL,0,NULL,NULL);
}

終わりに

テストと備忘録なので、今回は特になしです。

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5