標準與格式 | 金鑰檢查 | 非對稱式金鑰產生 | 加解密實作
Intro
標準與格式
編碼格式
格式 | 檔案內容 | 副檔名 | 標準來源 |
---|---|---|---|
DER (Distinguished Encoding Rules) |
Binary | .der .cer |
X.690 ASN.1 的編碼規則之一 |
PEM (Privacy Enhanced Mail) |
Base64 with BEGIN & END |
.pem .cert |
RFC 1421 |
OpenSSL 指令中預設的 format 都是使用 PEM,可使用 inform
或 outform
來指定輸出入格式:
# 編碼格式轉換公式
openssl <rsa|x509|pcks7> -inform <PEM|DER> -in <origin file> -outform <PEM|DER> -out <output file>
公鑰加密標準
PKCS #1
- RSA Cryptography Standard,為 RSA 基本公鑰及私鑰的傳統格式
- ASN.1 區分方式:為
prim: INTEGER
組成而無prim: OBJECT
- PEM 標頭區分方式:
- PKCS #1 Private Key:
-----BEGIN RSA PRIVATE KEY-----
- PKCS #1 Public Key:
-----BEGIN RSA PUBLIC KEY-----
- PKCS #1 Private Key:
PKCS #8
- Private-Key Information Syntax Standard,為通用私鑰格式,可另外加密 (passphrase)
- ASN.1 區分方式:相較於 PKCS#1 有
prim: OBJECT
提供演算法,通常第二列為prim: INTEGER :00
- 常見副檔名:
.p8
,.p8e
,.pk8
- PEM 標頭區分方式:
- PKCS #8 Unencrypted Private Key:
-----BEGIN PRIVATE KEY-----
- PKCS #8 Encrypted Private Key:
-----BEGIN ENCRYPTED PRIVATE KEY-----
- PKCS #8 Unencrypted Private Key:
# PKCS#1 private key 轉為 PKCS#8
$ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key-pkcs1.pem -out private-key-pkcs8.pem
# PKCS#8 轉為 PKCS#1 private key
$ openssl rsa -in private-key-pkcs8.pem -out private-key-pkcs1.pem
X.509
- 為公鑰憑證的格式標準,OpenSSL
-pubout
預設輸出的公鑰即為 X.509 Certificate Subject Public Key Info。可提取或轉換為 PKCS#1 公鑰 - ASN.1 區分方式:相較於 PKCS#8 通常第二列為
SEQUENCE
,亦有prim: OBJECT
提供演算法 - 常見副檔名:
.crt
: X.509 Certificate.pub
: X.509 Certificate Subject Public Key Info (僅包含公鑰)
- PEM 標頭區分方式:
- X.509 Certificate:
-----BEGIN CERTIFICATE-----
- X.509 Certificate Subject Public Key Info:
-----BEGIN PUBLIC KEY-----
- X.509 Certificate:
# 檢視查看 X.509 憑證內容
openssl x509 -noout -text -in cert.pem
# PKCS#1 private key 簽發 X.509 certificate
$ openssl req -new -key private-key-pkcs1.pem -out csr.pem
$ openssl x509 -req -in csr.pem -signkey private-key-pkcs1.pem -out certificate.pem
# X.509 提取公鑰轉為 X.509 public key
$ openssl x509 -in certificate.pem -pubkey -noout > public-key-x509.pem
# X.509 public key 轉為 PKCS#1 public key
$ openssl rsa -pubin -in public-key-x509.pem -RSAPublicKey_out > public-key-pkcs1.pem
# PKCS#1 public key 轉為 X.509 public key
$ openssl rsa -RSAPublicKey_in -in public-key-pkcs1.pem -pubout -out public-key-x509.pem
PKCS #7
- 是一種封裝數位簽名、對應憑證與原文數據(Optional)的格式,簽名的驗證需使用 X.509 格式的公鑰。
- 常見副檔名:
.p7s
: 數位簽章(含憑證),可附帶或不附帶原文數據,為 SignedData (sign) 格式.p7m
: 數位簽章(含憑證)且附帶原文數據,分為兩種格式:SignedData (sign) 格式 (即等同於.p7s
),及 EnvelopedData (encrypt) 格式.p7b
: 不含原文數據的 SignedData 結構,主要用於封裝憑證鏈(一個或多個 X.509 憑證),為常見的憑證檔案格式
- PEM 標頭區分方式:
- PKCS #7:
-----BEGIN PKCS7-----
- PKCS #7:
# 簽章 P7 by 指定憑證(簽章+附帶原文 -nodetach)
$ openssl smime -sign -in content.txt -signer certificate.crt -inkey private-key.pem -outform PEM -out signed_message.p7m -nodetach
# 驗簽+取出原文 (-out) noverify = 不驗證憑證鏈
$ openssl smime -verify -inform PEM -in signed_message.p7m -noverify -out extracted_content.txt
# 驗簽 by 指定憑證 (-CAfile) +取出原文
$ openssl smime -verify -inform PEM -in signed_message.p7m -out extracted_content.txt -CAfile certificate.crt
# 簽章 P7S by 指定憑證 (僅簽章不附帶原文)
$ openssl smime -sign -in content.txt -signer certificate.crt -inkey private-key.pem -outform PEM -out signed_message.p7s
# 驗簽指定原文 (-content) noverify = 不驗證憑證鏈
$ openssl smime -verify -inform PEM -in signed_message.p7s -content plaintext.txt -noverify
# 驗簽指定原文 by 指定憑證 (-CAfile)
openssl smime -verify -inform PEM -in signed_message.p7s -content plaintext.txt -CAfile certificate.crt
# 查看 PKCS#7 資訊 (-print) P7S 的 d.sign .contents .d.data 為 <ABSENT>
$ openssl pkcs7 -print -noout -in <PKCS7 file>
# 查看 PKCS#7 的憑證資訊 (-print_certs)
$ openssl pkcs7 -print_certs -text -in <PKCS7 file>
PKCS #12 / PFX
- 是一種包含私鑰與公鑰憑證/憑證鏈(public key certificates/chain)的格式,私鑰採用密碼 (passphrase) 保護
- 常見副檔名:
p12
,pfx
- PKCS#12 僅採用 DER (binary) 格式,若轉換為 PEM 格式即等於將私鑰與各個憑證分別儲存於同一個檔案中:
- 轉換後的 PEM 包含多個區塊如: 多個
-----BEGIN CERTIFICATE-----
與一個-----BEGIN PRIVATE KEY-----
- 轉換後的 PEM 包含多個區塊如: 多個
金鑰檢查
ASN1parse
使用 OpenSSL ASN.1 Parser,但須先知道編碼格式:
$ openssl asn1parse -in your-file.pem
# 利用 -inform 解析 DER 格式檔案
$ openssl asn1parse -inform DER -in your-file.der
result 若有 object 則可以快速識別演算法,如
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
pkey 輸出
使用預設 privateKey 解析工具,需先知道編碼格式及公/私鑰:
$ openssl pkey -text -noout -in private-key.pem
# 利用 -pubin 解析 Public key
$ openssl pkey -text -noout -pubin -in public-key.pem
根據演算法輸出
基於 pkey
,另外已知其演算法去解析:
# Read RSA Private Key
$ openssl rsa -text -noout -in private-key.pem
# Read EC Private Key
$ openssl ec -text -noout -in private-key.pem
# 利用 -pubin Read RSA Public Key
$ openssl ec -text -noout -pubin -in public-key.pem
# 利用 -inform 指定讀取 DER 格式
$ openssl rsa -text -noout -inform DER -in private-key.der
金鑰格式判定
PEM 有規範為 Text 且應有表頭 hyphen 符號如 -----BEGIN PUBLIC KEY-----
,DER 則為 binary。
基於以上,若收到公鑰是一組單行 base64 encoded text,可以驗證看看是不是 DER 直接做 base64 encoding:
$ echo 'MIIBIjANBgk.....QIDAQAB' | base64 -d | openssl asn1parse -inform DER
# base64 file to DER
$ cat public-base64.txt | base64 -d > public.der
如果上述是標準 base64 String (RFC 4648 §4),在知道演算法狀況下也可以直接用文字處理方式轉成 PEM: 頭與尾加上對應--KEY--格式,base64 string 每 64 字元換行
PEM 是使用標準 base64 String (RFC 4648 §4),不支援base64url (RFC 4648 §5)
非對稱式金鑰產生
OpenSSL
openssl 的通用操作指令都使用第一個參數來指定演算法,大多數版本可以用help
指令列出演算法列表,本篇會盡量使用pkey
通用指令來方便統一操作不同演算法的金鑰
$ openssl version
$ openssl help
產生一組金鑰 (私鑰+公鑰)
通用產生金鑰可以使用genpkey
指令,
$openssl genpkey -algorithm RSA -out private-key.pem
# 指定輸出格式為 DER
$openssl genpkey -algorithm RSA -outform DER -out private-key.pem
# 產生一組 ECDSA 金鑰
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp521r1 -out private-key.pem
# genpkey 預設產生私鑰格式為 PKCS #8
$openssl genpkey -algorithm RSA -out private-key.pkcs8.pem
# genrsa 預設產生私鑰格式為 RSA 傳統標準 PKCS #1
$openssl genrsa -out private-key.pkcs1.pem
-algorithm
,-pkeyopt
參數文件可以參考 openssl man & KEY GENERATION OPTIONS
genrsa
產出私鑰檔案標準為 PKCS #1,為早期給 RSA 專用較舊的標準
genpkey
產出私鑰檔案標準為 PKCS #8,為後期通用標準,可另外再加密
利用私鑰指定輸出公鑰
# 利用 -pubout 指定輸出公鑰
$ openssl pkey -in private-key.pem -pubout -out public-key.pem
# OpenSSL 現行版本預設 -pubout 為 X.509 Subject Public Key Info
$ openssl pkey -in private-key.any.pem -pubout -out public-key.x509.pem
$ openssl rsa -in private-key.any.pem -pubout -out public-key.x509.pem
# RSA 利用 `-RSAPublicKey_out` 指定輸出為 PKCS #1 Public Key
$ openssl rsa -in private-key.any.pem -pubout -RSAPublicKey_out -out public-key.pkcs1.pem
格式轉換
# DER to PEM for private key
$ openssl pkey -inform DER -in public.der -outform PEM -out public.pem
# DER to PEM for public key
$ openssl pkey -inform DER -pubin -in public.der -outform PEM -out public.pem
SSH-Key
使用 ssh-keygen
可以快速產生用於 SSH protocol 的公私鑰:
$ ssh-keygen
# 指定路徑與檔名
$ ssh-keygen -f $HOME/.ssh/id_rsa
Key Type
$ ssh-keygen -t [ dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa ]
$ ssh-keygen -t dsa
轉換成 PEM 格式
To PKCS#8:
$ ssh-keygen -f id_rsa -e -m PKCS8 > id_rsa.pem
SSH 公鑰也可以拿來轉換成 PKCS#8 PEM 即可相容於 OpenSSL 加解密
加解密實作
OpenSSL command
簡易產生公私鑰:
$ openssl genrsa -out private-key.pem
$ openssl rsa -in private-key.pem -pubout -out public-key.pem
目前 OpenSSL 中可以拿來作非對稱資料加解密的演算法主要為 RSA,算法支援公鑰私鑰雙向加解密。以下會直接使用usautl
指令做範例,usautl
跟pkeyutl
指令在此應用下是相同用法:
公鑰加密私鑰解密
加密 (使用public key):
$ openssl rsautl -encrypt -pubin -inkey public-key.pem -in plaintext.txt -out encrypted.txt
// pkeyutl
$ openssl pkeyutl -encrypt -in plaintext.txt -out ciphertext.bin -inkey public-key.pem -pubin
解密 (使用private key):
$ openssl rsautl -decrypt -inkey private-key.pem -in encrypted.txt -out plaintext.txt
// pkeyutl
$ openssl pkeyutl -decrypt -in ciphertext.bin -out decrypted.txt -inkey private-key.pem
私鑰加密公鑰解密 (利用數位簽章反向加解密)
加密 (使用private key):
$ openssl rsautl -sign -inkey private-key.pem -in plaintext.txt -out plaintext.txt.sign
解密 (使用public key):
$ openssl rsautl -pubin -inkey public-key.pem -in plaintext.txt.sign -out plaintext.txt
數位簽名簽署與驗證
$ openssl dgst -sha512 -sign private-key.pem -out digest.sha512 plaintext.txt
$ openssl dgst -sha512 -verify public-key.pem -signature digest.sha512 plaintext.txt
Verified OK
演算法選擇
更多指令選項包含演算法設定可以參考:openssl-pkeyutl man
# OAEP with SHA-256 and MGF1 with SHA1
$ openssl pkeyutl -encrypt -in plaintext.txt -out ciphertext.bin -pubin -inkey public-key-x509.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1
# OAEP with SHA-256 and MGF1 with SHA-256
$ openssl pkeyutl -encrypt -in plaintext.txt -out ciphertext.bin -pubin -inkey public-key-x509.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256
Programing Lanuage
公私鑰皆可以非對稱的加解密,例如PHP functions: - openssl_private_decrypt - openssl_private_encrypt - openssl_public_decrypt - openssl_public_encrypt