BackEnd

公開鍵暗号の概要、用語と使用例

投稿日:2022年9月26日 更新日:

はじめに

普段意識しなくても暗号化に関する技術は身近にあります。たとえば、SSL証明書、GitHubなどに登録する公開鍵、JWTトークンなどです。

私もこれらの技術を利用していますが、それがどのような仕組みで動いているかについては、あまり知りませんでした。

そこで、今回は暗号化について深堀りするとともに、実装の例としてJWTトークンの生成、署名、検証をGo言語で実装してみたいと思います。

前提条件

一口に暗号化といっても様々な種類があります。そこで今回は、一般的によく使われるであろうRSA暗号に限定し、暗号鍵の種類もOpenSSH形式、PKCS #1およびPKCS #8に限定します。

また、Go言語のバージョンは、1.19.1を使用します。

暗号化と復号

それではまずはじめに、暗号化および復号についてざっくり説明します。

暗号化とは

暗号化とは、第三者に不正にデータを見られることを防ぐために、暗号鍵というデータを使用してデータを解読できない形に加工することです。この変換前のメッセージを「平文」といい、加工後のデータを「暗号文」といいます。

復号とは

複合とは暗号化の逆、つまり「暗号文」を加工して元の「平文」に戻す行為です。この時も暗号鍵を使用しますが、暗号の種類によっては、暗号化に使用する暗号鍵と複合に使用する暗号鍵は異なる場合があります(RSA暗号など)。

暗号化方式

暗号化方式には共通鍵暗号と公開鍵暗号の2種類があります。

共通鍵暗号

共通鍵暗号(対称鍵暗号とも言う)では、その名の通り暗号化と複合に同じ暗号鍵(共通鍵、対称鍵とも言う)を使用します。

共通鍵暗号で使用するアルゴリズムには「RC4、DES、3DES、AES」などがあり、現在の主流は「AES」です。

共通鍵暗号では、接続先ごとに共通鍵を生成する必要があります。共通鍵が盗まれてしまうと暗号文を複合できてしまうので、鍵交換を盗聴されないように安全に行う必要があります。

公開鍵暗号

公開鍵暗号(非対称暗号とも言う)では、暗号化と複合に別の暗号鍵を使用します。このとき、暗号化に使用する鍵を公開鍵といい、復号化に使用する鍵を秘密鍵といいます。

公開鍵暗号で使用するアルゴリズムには「RSA、EIGamal」などがあります。

公開鍵暗号では、受信者側にて公開鍵と秘密鍵を生成します。

署名と検証

公開鍵暗号に関わる署名および検証について説明します。

署名とは

電子文書に対して付与される署名です。その電子文書が正規なものであり、かつ、改ざんされていないことを証明するものです。

検証とは

電子署名の有効性を確認するための行為のことです。

具体的には、「電子署名が付与されたデータが改竄されていないか」、「電子署名が有効であるか」、「電子署名の信頼性が確認されているか」などを確認します。

RSA暗号とは

RSA暗号とは公開鍵暗号の一つで、素因数分解の難しさを利用した暗号アルゴリズムです。RSAは製作者の名前に由来しています。

RSA暗号は、整数論の定理であるオイラーの定理と2つの素数を使用して公開鍵を作成しており、膨大な数の素因数分解が困難なことが安全性の根拠となっています。

RSA暗号の暗号鍵の形式については冒頭で記載したとおり、OpenSSH形式、PKCS #1およびPKCS #8に絞って説明します。

OpenSSH形式

OpenSSH形式はOpenSSH独自の暗号鍵のフォーマットです。この形式の暗号鍵は、ssh-keygenコマンドで生成することができます。

OpenSSH形式の公開鍵

OpenSSH形式の公開鍵は以下のように、ssh-rsaから始まるのが特徴です。概ねRFC4716の形式と同じですが、ヘッダは含まれず改行もされていないOpenSSH独自の形式のようです。

OpenSSH形式の秘密鍵

OpenSSH形式の秘密鍵は、OpenSSH7.8以降とそれより前で形式が異なります。OpenSSH7.8より前のOpenSSH形式の秘密鍵はOpenSSLの形式(おそらくPKCS #1)と同じです。それに対し、OpenSSH7.8以降のOpenSSH形式の秘密鍵はOpenSSLの秘密鍵とは互換性がありません。


10月30日補足

秘密鍵の新形式はOpenSSH6.5にて楕円曲線署名スキーマの「Ed25519」がサポートされた際に追加されたようです。この「Ed25519」は新形式のみ対応で、RSAの場合は新形式に加えて旧形式を使うこともできます。


現在はssh-keygenで作成した秘密鍵の形式は、OpenSSH7.8以降の秘密鍵の形式がデフォルトとなっており、作成した秘密鍵はこの様になっています。

PKCS #1

PKCS #1の説明の前に、まずはPKCSとは何かについて説明します。

PKCS(Public-key Cryptography Standards)とは、RSAセキュリティにより考案させれた各種の技術仕様を定めた規格群です。PKCSには#1から#15まであり、それぞれRSA暗号に関する仕様が定められています。そのうちPKCS #1はRSA暗号の仕様を定めたものになります(最新はRFC 8017)。

このRFC 8017で定めている仕様とは具体的に、

  • 暗号プリミティブ
  • 暗号化スキーム
  • (付録付き)署名スキーム(signature scheme with appendix)

などです。これらについては次に説明します。

また、RFC 8017ではAppendixにて、RSA鍵の表現として公開鍵と秘密鍵のANS.1オブジェクト識別子とRSAPublicKey型およびRSAPrivateKey型を定義しています。

ANS.1については後ほど改めて説明します。また、以下の暗号プリミティブおよび暗号化スキーム、(付録付き)署名スキームの説明については、RSA暗号の定義となります。

暗号プリミティブ

暗号プリミティブとは、暗号スキームを構築するための基本的な数式のことです。暗号プリミティブには以下の4つの種類のプリミティブがあります。

  • 暗号化プリミティブ
  • 複合化プリミティブ
  • 署名プリミティブ
  • 検証プリミティブ

これらのうち、暗号化プリミティブと復号化プリミティブ、署名プリミティブと検証プリミティブはペアになっています。

暗号化プリミティブは、公開鍵を用いてメッセージから暗号文を生成します。そして、復号化プリミティブは、秘密鍵を用いて暗号文からメッセージを復元します。これら2つは暗号化スキームで使用されます。

同様に、署名プリミティブは、秘密鍵を使ってメッセージから署名を生成します。そして、検証プリミティブは、公開鍵を使って署名からメッセージを復元します。これら2つは(付録付き)署名スキームで使用されます。

暗号化スキーム

暗号化スキームは、暗号化操作と復号化操作で構成されます。

暗号化操作では、受信者のRSA公開鍵を含むメッセージから暗号文が生成されます。また、復号化操作では、受信者のRSA秘密鍵を使って暗号文からメッセージを復元します。

署名スキーム

(付録付き)署名スキームは署名生成操作と署名検証操作で構成されています。

署名生成操作では、署名者のRSA秘密鍵を含むメッセージから署名が生成ハッシュ値から署名が生成(10月30日訂正)されます。また、署名検証操作では、署名者のRSA公開鍵を使って署名が検証されます。

(付録付き)署名スキームで構築された署名を検証するには、メッセージ自体が必要となります。

(付録付き)署名スキームは、X.509証明書などの様々な用途に使用できます。

ASN.1

ASN.1は情報の抽象構文です。定義の対象用途にはX.509証明書、PKCS #1、PKCS #8およびPKCS #12などがあげられます。

ANS.1では名前と型によって定義されるオブジェクトを列挙してデータ構造を定義し、定義された構文を用いて具体的な値を持つインスタンスを記述することができます。

例えば、「Price:==INTEGER」という記述により、integer型の値をもつPriceという名前のデータを定義することができます。

また、定義された構文に従って記述されたデータを特定のバイト列へ変換する方法もいくつか定められており、代表的なものとしてはBERやDERなどがあります。

PKCS #8

PKCS #8は、RSAに限らず全ての種類の秘密鍵を処理するための仕様を定めたものです(最新はRFC 5958

秘密鍵情報および暗号メッセージ構文(CMS)コンテンツタイプのための構文を定義します。このCMS(Cryptographic Message Syntax)はRFC 5652で定義されています。

CMSはデジタル署名、ダイジェスト、認証、または非対称鍵形式コンテンツタイプを暗号化するために使用することができます。

PEMとDER

PEMとDERはどちらも証明書や暗号鍵の入れ物として使われます。両者の違いはエンコーディングと複数の証明書等のまとめ方です。

PEMは証明書や鍵をBase64エンコードし、それぞれを平文のヘッダーとフッターで囲います。

DERは証明書や鍵をバイナリエンコードします。DERでエンコードされたファイルには、PEMとは異なりヘッダーとフッダーは含まれません。

実際に署名と検証を行ってみる

それでは、実際にプログラムで動かしてみましょう。まずはじめにJWTトークンを生成します。JWTトークンの生成にはjwt-goを使用しました。また、このサンプルではPKCS #1形式の公開鍵、秘密鍵をJWTトークンの暗号化および復号署名および検証(10月30日訂正)に使っています。

JWTトークンの生成

JWTトークンの生成ではjwt.NewWithClaims()メソッドを使用します。引数としてjwt.SignningMethodjwt.RegisteredClaimsを渡します。その後、生成したJWTトークンをパースした秘密鍵で暗号化署名(10月30日訂正)します。

秘密鍵のパースはこのように行います。

JWTトークンの検証

生成したJWTトークンを検証します。jwt.Parse()メソッドではJWTトークンの文字列を解析、検証し、jwt.Token型の構造体のポインタを返します。

parse()メソッドに渡しているkeyはPKCS #1形式の公開鍵で、このようにパースしています。

また、サンプルコードの全体はこちらをご確認ください。

さいごに

暗号化技術に関わる製品やサービスなどは生活の中に溶け込んでいますが、どのような技術なのかについてはあまり意識していませんでした。今回それらを調べることで暗号化技術の奥深さの一端を知ることができたのではないかと思います。

おすすめ書籍

図解即戦力 暗号と認証のしくみと理論がこれ1冊でしっかりわかる教科書  現代暗号技術入門

blog-page_footer_336




blog-page_footer_336




-BackEnd
-,

執筆者:

         

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


  1. @angel_p_57(twitter) より:

    幾つか誤解があるように見えます。

    > 実際に暗号化を行ってみる
    JWTの操作は署名なので、「暗号化を行う」だと操作目的が全然合ってません。
    ※なんのためにその上で暗号化と署名の話を両方挙げたのか、意味がなくなります

    > 同様に、署名プリミティブは、秘密鍵を使ってメッセージから署名を生成します。
    署名プリミティブ・検証プリミティブは、メッセージからハッシュを計算した後の話なので「ハッシュ値から署名を生成」です。

    > OpenSSH形式の秘密鍵は、OpenSSH7.8以降とそれより前で形式が異なります。
    > OpenSSH7.8以降の秘密鍵の形式がデフォルト

    デフォルトが変わったのはその通りですが、経緯にたいしてちょっと誤解を生む表現です。
    新形式は ed25519用にOpenSSH6.5 で登場したもので、ed25519 は当初から新形式しか使えません。で、RSA等は新旧どちらでも使えましたが、作成時のデフォルトは旧形式でした。で、OpenSSH7.8 で作成時のデフォルトが新形式に変わっています。( 依然旧形式を使うことも作ることもできます )

    • Hiroki Ono より:

      ご指摘ありがとうございます。
      該当箇所を修正しました。また、OpenSSHの新形式の件勉強になりました。

Hiroki Ono へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

laravel logo

Laravelでテストコードを書くには? Featureテスト/Unitテスト

1 はじめに2 FeatureとUnitの使い分け3 テスト用データベースの準備4 Featureテスト4.1 テスト対象のコード4.2 テストコードの実装4.3 テスト結果の検証4.3.1 ステータ ...

麻雀で自分が何を切るか学習させる

1 はじめに2 プロジェクト名を決めてみる3 何切る問題で考慮される要素3.1 手牌3.2 ドラ3.3 考慮していない要素(本当は入れたい)4 学習の方法5 さいごに はじめに 前回執筆しました、配牌 ...

Rust入門してみた (基本構文編)

1 はじめに2 Rustとは?3 Rustの特徴的な基本構文3.1 変数と定数3.2 所有権3.3 所有権の借用3.4 関数3.5 エラーハンドリング3.5.1 回復不能なエラー(panic!)3.5 ...

Go言語

GoフレームワークGinでミドルウェアを使ってログインAPIを実装

1 はじめに2 ログインAPIの作成3 ログインのセッション管理4 ミドルウェア4.1 gin.Default()4.2 Logger4.3 Recovery4.4 sessions5 独自ミドルウェ ...

rails

ShrineでS3に画像をアップロードする

1 はじめに1.1 前提条件1.2 関連記事2 AWS側の準備2.1 S3バケットを作成する2.2 CORSを設定する2.3 アクセス用のユーザを作成する3 Rails側の設定3.1 Initiali ...

フォロー

follow us in feedly

blog-page_side_responsive

2022年9月
 123
45678910
11121314151617
18192021222324
252627282930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。