はじめに
この記事では、テスト目的で適切なX.509証明書を改ざんして不正なX.509証明書にする方法を紹介します。
改ざんというと悪いイメージがどうしてもありますが、あくまでもテストが目的ですのでご留意ください。
なぜこんな記事を?
不正なX.509証明書を検証して拒否できるか、というテストをしたい時があると思います(私はありました)。
そのために不正な証明書をOpenSSLでうまいこと作れないかなと思いましたが、そう簡単にはいきませんでした。
最終的には作ることができたのですが、何をどうすれば作れるのか試行錯誤した経験を元に何かアウトプットしておこうと思ったのがきっかけです。
前提
- 署名アルゴリズムはsha256WithRSAEncryptionで、RSA鍵長は2048bitです
- PKCS#7やPKCS#12に含まれている証明書をそのままの状態で改ざんするようなケースは考慮していません
- 記載しているコマンドは全て、Macのターミナル上で実行しています
必要なもの
- X.509証明書のファイル
- テキストエディタあるいはバイナリエディタ
X.509証明書のどこを改ざんするのか
今回は署名のフィールドを改ざんします。
そのためにはX.509証明書のフォーマットに関する知識が必要ですが、本記事に記載していることをやるだけならあまり必要ありません。
以下のリンク先はRFC 5280(X.509v3)で証明書のフォーマットが記載されているセクションですが、3つあるフィールドのうち最後のsignatureValueというフィールドを改ざんします。signatureValueは署名データそのものなので、ここを改ざんすれば署名がおかしい証明書となるわけです。
https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
実際にやってみる
以下のような証明書を改ざんしてみます。
この証明書はMacにインストールされていたLibreSSLで作りましたが、どんな証明書でもやることは変わらないので、テスト用のX.509証明書をご自身で用意してください。
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=JP, CN=mid
Validity
Not Before: Aug 17 07:22:23 2025 GMT
Not After : Aug 17 07:22:23 2026 GMT
Subject: C=JP, CN=Lyxion
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:b6:df:a1:c3:4e:ac:86:63:c0:53:9c:9f:76:51:
90:33:9c:72:1f:ba:4e:36:2f:d0:da:48:c1:c2:4f:
fc:ff:41:94:52:55:ec:91:63:19:ee:2a:b5:28:85:
4c:15:c2:f1:75:86:3e:8e:63:4e:49:5d:f8:ee:9a:
82:8e:12:5d:dd:50:e5:16:ca:5b:be:dc:37:ef:44:
94:6e:fd:4f:03:5f:0f:75:ed:9b:5b:57:b8:ed:10:
c4:db:a5:79:79:24:89:64:4c:08:db:8b:05:27:04:
9c:13:5a:07:b0:fd:5f:29:26:d5:50:14:32:f3:e8:
99:ed:6a:79:fc:3f:c5:cd:57:bc:9b:7c:e1:7a:97:
f5:b4:ed:4d:84:a4:a8:de:86:ea:8e:73:7e:71:20:
3f:b8:8a:0e:31:89:61:4c:c2:59:84:7d:b7:12:9f:
7f:e6:c6:8d:02:a2:c6:eb:53:26:d5:81:82:1b:0b:
75:83:5d:f5:75:f4:cc:e8:94:37:c4:25:fc:d1:0d:
bb:2f:0c:48:4b:a3:cc:58:58:3e:13:b6:15:a6:63:
a6:07:54:99:fe:81:55:42:ea:26:47:d3:b4:b8:8f:
92:23:a3:a4:38:78:a5:0a:ce:46:5b:65:81:e7:28:
c8:73:dd:aa:8e:bc:cb:0b:1d:c3:f2:46:94:71:8c:
35:b5
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
3E:85:7A:77:90:19:2F:CB:C8:78:54:54:76:67:1E:91:5A:17:5C:FA
X509v3 Authority Key Identifier:
keyid:B9:23:D3:07:42:BD:89:D5:DB:10:EB:73:A1:9D:8E:CE:F9:52:7E:BD
Signature Algorithm: sha1WithRSAEncryption
b6:b3:a7:8d:dd:7d:8d:fb:cb:11:3d:9f:5e:cb:46:47:d0:65:
bb:27:55:06:45:c0:e5:a3:c2:99:be:6e:9c:47:a8:07:52:fd:
34:31:e7:c6:99:96:13:7e:21:63:1e:6f:e8:49:e1:90:df:62:
f2:cd:89:cc:3f:9c:03:6c:50:f2:fa:59:c3:ef:b6:ed:73:fe:
46:57:c4:f2:ef:9f:a0:5a:b7:4b:b3:a3:5d:c0:7e:27:b7:8d:
38:1f:80:2b:2c:00:8f:a8:76:fa:35:f2:e0:d5:b3:0e:d7:6e:
75:6b:3b:37:53:66:92:4f:73:1e:9d:60:ef:5e:bf:fb:1f:09:
7a:a3:47:e3:a2:5b:01:47:28:06:78:6b:47:d2:e7:c2:a5:7d:
be:d2:2b:8f:62:c5:f3:68:95:4d:c9:de:15:81:17:d1:13:25:
c7:6e:25:b4:fe:29:f0:00:69:a2:ae:f8:d8:b1:fe:60:60:cf:
01:9f:eb:63:0c:00:91:b5:ef:c8:a6:61:ae:3d:65:8c:30:3c:
43:02:81:34:eb:f3:e8:02:0f:3c:4d:56:6f:f4:c7:9b:24:dd:
73:4c:27:6d:f4:4d:d3:a9:e2:36:66:80:84:e1:be:f8:2c:0e:
99:d2:06:31:8e:ab:a7:d7:1e:3a:3e:26:01:82:19:4b:74:05:
78:51:99:cc
ここから先は実際に改ざん処理を進めますが、その前に証明書の形式を確認します。
形式はPEMかDERになっていると思いますので、fileコマンドで証明書を調べてみてください。
以下のように表示されていればPEM形式です。
% file cert.pem
cert.pem: PEM certificate
そうでなければDER形式のはずです。
※PEMとDER以外があったらすみません。
% file cert.crt
cert.crt: , Serial=009c3747eb7247ca3e, not-valid-before=2025-08-12 05:21:50 GMT, not-valid-after=2025-09-11 05:21:50 GMT
PEM形式の場合
最初に、編集する前のファイルをバックアップしておいてください。
PEM形式では、証明書データはbase64エンコードされ、テキストエディタで編集可能な状態になっていますので、テキストエディタで証明書ファイルを開きます。
先頭の行が"-----BEGIN CERTIFICATE-----"、一番最後の行が"-----END CERTIFICATE-----"となっていますが、これは目印であって、実際のデータはそれらに挟まれている部分です。
-----BEGIN CERTIFICATE-----
MIIDLzCCAhegAwIBAgIBADANBgkqhkiG9w0BAQUFADAbMQswCQYDVQQGEwJKUDEM
MAoGA1UEAwwDbWlkMB4XDTI1MDgxNzA3MjIyM1oXDTI2MDgxNzA3MjIyM1owHjEL
MAkGA1UEBhMCSlAxDzANBgNVBAMMBkx5eGlvbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALbfocNOrIZjwFOcn3ZRkDOcch+6TjYv0NpIwcJP/P9BlFJV
7JFjGe4qtSiFTBXC8XWGPo5jTkld+O6ago4SXd1Q5RbKW77cN+9ElG79TwNfD3Xt
m1tXuO0QxNuleXkkiWRMCNuLBScEnBNaB7D9Xykm1VAUMvPome1qefw/xc1XvJt8
4XqX9bTtTYSkqN6G6o5zfnEgP7iKDjGJYUzCWYR9txKff+bGjQKixutTJtWBghsL
dYNd9XX0zOiUN8Ql/NENuy8MSEujzFhYPhO2FaZjpgdUmf6BVULqJkfTtLiPkiOj
pDh4pQrORltlgecoyHPdqo68ywsdw/JGlHGMNbUCAwEAAaN7MHkwCQYDVR0TBAIw
ADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUw
HQYDVR0OBBYEFD6FeneQGS/LyHhUVHZnHpFaF1z6MB8GA1UdIwQYMBaAFLkj0wdC
vYnV2xDrc6Gdjs75Un69MA0GCSqGSIb3DQEBBQUAA4IBAQC2s6eN3X2N+8sRPZ9e
y0ZH0GW7J1UGRcDlo8KZvm6cR6gHUv00MefGmZYTfiFjHm/oSeGQ32LyzYnMP5wD
bFDy+lnD77btc/5GV8Ty75+gWrdLs6NdwH4nt404H4ArLACPqHb6NfLg1bMO1251
azs3U2aST3MenWDvXr/7Hwl6o0fjolsBRygGeGtH0ufCpX2+0iuPYsXzaJVNyd4V
gRfREyXHbiW0/inwAGmirvjYsf5gYM8Bn+tjDACRte/IpmGuPWWMMDxDAoE06/Po
Ag88TVZv9MebJN1zTCdt9E3TqeI2ZoCE4b74LA6Z0gYxjqun1x46PiYBghlLdAV4
UZnM
-----END CERTIFICATE-----
今回は末尾から4文字目の「U」を変更して「A」にしてみます。
base64では、データを表現するために使える文字が、英大文字(A-Z)、英小文字(a-z)、数字(0-9)、プラス(+)、スラッシュ(/)の64種と決まっていて、「A」である必要はありません。使える64種の文字から今と違う文字を選べばOKです。
※わざわざ末尾から4文字目を選択した理由は後で補足します。
以下、変更後のPEM形式のデータです。
-----BEGIN CERTIFICATE-----
MIIDLzCCAhegAwIBAgIBADANBgkqhkiG9w0BAQUFADAbMQswCQYDVQQGEwJKUDEM
MAoGA1UEAwwDbWlkMB4XDTI1MDgxNzA3MjIyM1oXDTI2MDgxNzA3MjIyM1owHjEL
MAkGA1UEBhMCSlAxDzANBgNVBAMMBkx5eGlvbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALbfocNOrIZjwFOcn3ZRkDOcch+6TjYv0NpIwcJP/P9BlFJV
7JFjGe4qtSiFTBXC8XWGPo5jTkld+O6ago4SXd1Q5RbKW77cN+9ElG79TwNfD3Xt
m1tXuO0QxNuleXkkiWRMCNuLBScEnBNaB7D9Xykm1VAUMvPome1qefw/xc1XvJt8
4XqX9bTtTYSkqN6G6o5zfnEgP7iKDjGJYUzCWYR9txKff+bGjQKixutTJtWBghsL
dYNd9XX0zOiUN8Ql/NENuy8MSEujzFhYPhO2FaZjpgdUmf6BVULqJkfTtLiPkiOj
pDh4pQrORltlgecoyHPdqo68ywsdw/JGlHGMNbUCAwEAAaN7MHkwCQYDVR0TBAIw
ADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUw
HQYDVR0OBBYEFD6FeneQGS/LyHhUVHZnHpFaF1z6MB8GA1UdIwQYMBaAFLkj0wdC
vYnV2xDrc6Gdjs75Un69MA0GCSqGSIb3DQEBBQUAA4IBAQC2s6eN3X2N+8sRPZ9e
y0ZH0GW7J1UGRcDlo8KZvm6cR6gHUv00MefGmZYTfiFjHm/oSeGQ32LyzYnMP5wD
bFDy+lnD77btc/5GV8Ty75+gWrdLs6NdwH4nt404H4ArLACPqHb6NfLg1bMO1251
azs3U2aST3MenWDvXr/7Hwl6o0fjolsBRygGeGtH0ufCpX2+0iuPYsXzaJVNyd4V
gRfREyXHbiW0/inwAGmirvjYsf5gYM8Bn+tjDACRte/IpmGuPWWMMDxDAoE06/Po
Ag88TVZv9MebJN1zTCdt9E3TqeI2ZoCE4b74LA6Z0gYxjqun1x46PiYBghlLdAV4
AZnM
-----END CERTIFICATE-----
これでPEM形式の証明書の署名データを改ざんできました。
DER形式の場合
こちらも編集する前に、ファイルをバックアップしておいてください。
DER形式のファイルは中身がバイナリ列になっていて、PEM形式のようにテキストエディタで直接編集できません。
そのため、今回はxxdコマンドとvimエディタを利用してバイナリファイルを編集してみます。
※vimエディタの基本的な使用方法は割愛します。また、使えるバイナリエディタがあればそれを使ってください。
まずはDER形式の証明書をvimのバイナリモードで開きます。
% vim -b cert.crt
開いた直後は文字化けしまくっていると思いますが、これを意味のある文字列に変換します。
ESCキーを押し、続けてコロン(:)キーを押すとコマンドラインモードに移行したら、画面の左下にコロンが表示されます。その状態で以下のコマンドを入力してEnterキーで実行します。
意味は「ファイル全体(%)に外部コマンド(!)であるxxdを適用する」です。
※先頭のコロンはコマンドラインモードに移行したら表示されますので、画面上で以下のようになればOKです。
:%!xxd
うまくいけば、16進ダンプが表示されるはずです。
% xxd EE_cert.crt
00000000: 3082 032f 3082 0217 a003 0201 0202 0100 0../0...........
00000010: 300d 0609 2a86 4886 f70d 0101 0505 0030 0...*.H........0
00000020: 1b31 0b30 0906 0355 0406 1302 4a50 310c .1.0...U....JP1.
00000030: 300a 0603 5504 030c 036d 6964 301e 170d 0...U....mid0...
(中略)
00000320: 318e aba7 d71e 3a3e 2601 8219 4b74 0578 1.....:>&...Kt.x
00000330: 5199 cc Q..
ここで表示されている一番最後のバイトである「cc」を「cd」にしてみます。hexであればなんでもいいので、「00」とかでもOKなはずです。
% xxd EE_cert.crt
00000000: 3082 032f 3082 0217 a003 0201 0202 0100 0../0...........
00000010: 300d 0609 2a86 4886 f70d 0101 0505 0030 0...*.H........0
00000020: 1b31 0b30 0906 0355 0406 1302 4a50 310c .1.0...U....JP1.
00000030: 300a 0603 5504 030c 036d 6964 301e 170d 0...U....mid0...
(中略)
00000320: 318e aba7 d71e 3a3e 2601 8219 4b74 0578 1.....:>&...Kt.x
00000330: 5199 cd Q..
ここまでできたら、またESCキー、コロンキーと押してコマンドラインモードに移行して、以下のコマンドを実行して見た目を元に戻します。
:%!xxd -r
※こうしないと、保存した時に画面の見た目通りの16進ダンプが記載されたテキストファイルになってしまいます
最後に、:wqでファイルを保存して終了してください。
DER形式はこれで改ざん完了です。
補足:PEM形式のファイルで、末尾から4文字目を変更した理由
PEM形式で記録されているデータはbase64エンコードが施されていますが、末尾の1バイトのデータを表現するには少々ややこしいルールがあります。
末尾から4文字目ならばそれを考えなくても良くなる、というのが理由です。
※これ以上踏み込むとbase64の細かい話になってしまい、本題から逸れてしまうのでやめておきます
まとめ
本記事ては、X.509証明書の署名フィールドを書き換えて署名検証に失敗する不正な証明書を作る方法について示しました。
また、別途なんらかのツールを導入しなくてもよいように、もともとインストールされているようなコマンドでなるべく進められるような内容とし、PEM形式とDER形式両方の証明書を改ざんする方法を示しました。
あとがき
どうしてもこういうテストをしなくてはならなくなった時に詰んでしまう人を減らせたらいいなと思います。
改めてではありますが、改ざんを施した証明書を作って自分の管理外のノードに送りつけたりするような行為は決してしないようにお願いします。
最後まで読んでくださりありがとうございました。
参考記事
いくつか参考にさせてもらったページがあります。ぜひ併せて読んでみてください。
図解 X.509 証明書 - Qiita
https://qiita.com/TakahikoKawasaki/items/4c35ac38c52978805c69
vimでバイナリを編集する - Qiita
https://qiita.com/urakarin/items/337a0433a41443731ad0
Base64 - Wikipedia
https://ja.wikipedia.org/wiki/Base64
余談
同じくテストのため、RSA公開鍵のフィールドでも同様の操作をしてみたのですが、うまくいきませんでした。もっと勉強が必要ですね。