SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

VPS 如何檢查與恢復 AES-NI

用 1 個指令確認 VPS 是否暴露 AES-NI,測量 CPUID 遮蔽對 AES-GCM 吞吐量的影響,並透過 OPENSSL_ia32cap 嘗試恢復硬體加速。

VPS 上的 AES-NI 實際能帶來什麼

VPS 上的 AES-NI 是一組由 6 個 x86 指令組成的功能,可在硬體中執行 AES(advanced encryption standard)的一個回合。如果供應商隱藏了 CPU 型號所支援的這些指令,底層的實體 CPU 仍然具備相關功能,但 OpenSSL 無法偵測到,因而退回軟體實作,每個位元組所需的週期數約增加 10 倍。你可以用 1 個指令檢查這項功能,用 2 個指令測量差距,通常也能透過 1 個環境變數重新啟用快速路徑。

這些指令是 AESENCAESENCLASTAESDECAESDECLASTAESIMCAESKEYGENASSIST。Intel 於 2010 年推出這些指令,AMD 隨後跟進,因此你可能租用的任何伺服器 CPU 幾乎都具備相關硬體。配套指令 PCLMULQDQ 可執行無進位乘法,這是 GCM(Galois/counter mode)建立驗證標籤所需的功能。只有兩者都可用時,AES-GCM 才能快速運作,因為加密演算法與驗證標籤是分開處理的工作。

在 VPS 上,以下 4 個情境會在監控資料中反映出這項差異:

  • TLS(transport layer security)termination。提供 AES-128-GCM 或 AES-256-GCM 的 Web server,大部分的對稱式加密處理時間都耗在 AES 中。
  • 加密磁碟區。LUKS(Linux unified key setup)和 dm-crypt 會在 kernel 中,於 CPU 上對每次讀取與寫入執行 aes-xts
  • 以 AES 為基礎的 VPN 流量。使用 AES-256-GCM 的 OpenVPN,以及採用 AES-GCM 的 IPsec,都高度依賴這項功能。
  • 加密備份。任何在資料離開主機前,先以 AES 加密資料流的服務,都會承擔相同成本。

有一種常見工作負載完全不受影響。WireGuard 的資料傳輸使用 ChaCha20-Poly1305,完全不使用 AES。因此,在遮蔽該旗標的主機上,自架 WireGuard VPN 的運作速度相同。這項差異是選擇低價 VPS 的 tunnel 前,應比較 WireGuard 與 OpenVPN 的實際理由。

如何檢查 VPS 是否支援 AES-NI

核心會將 CPUID 功能位元複製到 /proc/cpuinfo,因此執行一次 grep 即可確認。

grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'

任一命令輸出 aes,都表示 CPU 向此 guest 宣告支援 AES-NI。沒有任何輸出則表示不支援。lscpu 讀取相同的旗標,因此兩者結果一定一致。使用已安裝的命令即可。

接著查看主機宣稱你正在使用哪一部 CPU。

grep -m1 'model name' /proc/cpuinfo

輸出 Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHzAMD EPYC 7443P 24-Core Processor 這類真實型號字串,表示主機將實體 CPU 型號傳遞給你。輸出 QEMU Virtual CPU version 2.5+Common KVM processor 則表示發生其他情況,而這正是值得了解的案例。

晶片具備該功能,為何旗標仍然缺少

CPUID 是程式用來查詢 CPU 支援哪些功能的指令。在虛擬機器中,CPUID 一律會陷入 hypervisor,因此由 hypervisor 決定要向 guest 回報哪些資訊。大多數控制面板會將這項決定呈現為 guest CPU model。qemu64kvm64 是通用基準模型,兩者的功能集都不包含 AES-NI 或 SSSE3,因此即使實體主機使用新款 EPYC,guest 仍看不到 aes 旗標。VPS 是執行在他人硬體上的 guest,因此它回報的每項功能,都是上層所做的決定。如果你不熟悉這種分層方式,請先閱讀 VPS 是什麼

主機會刻意選用通用模型,因為只有在 guest 從未得知目的地主機缺少的功能時,才能在處理器不同的機器之間進行 live migration。代價由你承擔。你的 kernel 與 OpenSSL 副本都會在啟動時讀取一次經過遮罩的 CPUID,之後在該程序的整個生命週期中,都會選用較慢的程式碼路徑。

根本解法是從主機端設定:以 QEMU 的術語來說,使用 -cpu host、選擇包含 AES-NI 的具名模型,或在模型中明確加入 +aes。你無法從 guest 內部設定這些項目。提交支援請求,或選擇 hypervisor 會傳遞 CPU model 的方案,才是持久的解決方式。

使用 openssl speed 測量差距

不要盲信已發布的基準測試。請執行自己伺服器實際協商使用的 cipher。

openssl version
openssl speed -evp aes-128-gcm

結果列標示為 AES-128-GCM,並以每秒 1000 bytes 為單位,提供 6 種區塊大小的 throughput。大量傳輸應讀取 8192-byte 欄位,因為 16-byte 欄位主要受每次呼叫的額外負擔影響,無法反映檔案下載效能。

現在在軟體中關閉 AES-NI 與 PCLMULQDQ,再執行相同的 command:

OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcm

該數值來自 OpenSSL 自身的 capability vector 文件。開頭的 ~ 表示「清除這些 bits」。第 57 bit 是 AES-NI,第 33 bit 是 PCLMULQDQ,因此 0x200000200000000 只會指定這兩項,不包含其他項目。第二個數值若遠低於第一個,表示主機上的 AES-NI 正常運作,測試即可結束。若兩個數值相同,表示 OpenSSL 原本就已使用 software path,因為原本沒有該 flag 可供清除。

ChartAES-128-GCM throughput, one core, 8192-byte blocks (representative published figures)
The data behind this chart
[
  {
    "label": "AES-NI and PCLMULQDQ",
    "mb_per_sec": "4,850",
    "cycles_per_byte": 0.7
  },
  {
    "label": "Software fallback",
    "mb_per_sec": 310,
    "cycles_per_byte": 11.0
  }
]

這些是現代 x86 core 在接近 3.4 GHz 時的代表性已發布數據,不是從任何單一 host 取得的測量結果。請將其視為效能形狀。hardware path 約為每 byte 0.7 個 cycles,software fallback 約為 11.0 個 cycles,換算後,單一 core 約可達 4,850 MB/s,相較之下為 310 MB/s。上方兩個 command 產生的數值,才是描述你伺服器的唯一結果。同樣的原則也適用於機器的其他部分,因此在判斷方案前,請搭配 可重複的 VPS 基準測試方法 使用。

使用 OPENSSL_ia32cap 強制重新啟用指令集

這裡是最容易讓人意外的部分。AES-NI 指令不需要特權,hypervisor 也不會攔截這些指令。只有 CPUID 會被攔截。因此,主機可以告訴 guest AES-NI 不存在,但 AESENC 仍會以原生速度全速執行。軟體因為查詢 CPUID 後取得錯誤答案,而略過快速路徑。指令本身從未停止運作。

OpenSSL 可代表 CPU 提供答案。在 OPENSSL_ia32cap 中設定單純的十六進位值,會覆寫 capability vector,而不是套用遮罩。

OPENSSL_ia32cap="0x0200020207000000" openssl speed -evp aes-128-gcm

如果這次執行速度比一般執行快數倍,表示硬體具備 AES-NI,而主機正在隱藏它。這首先是診斷方法。對 OpenSSL 而言,它也正好可以作為修正方法。

如何組成這個十六進位值

第一個 logical vector 會將 CPUID leaf 1 的 EDX 放入低 32 bits,並將 leaf 1 的 ECX 放入高 32 bits。在低半部,bit 24 是 FXSR、bit 25 是 SSE、bit 26 是 SSE2,因此得到 0x07000000。在高半部,bit 33 是 PCLMULQDQ、bit 41 是 SSSE3、bit 57 是 AES-NI,因此得到 0x02000202。合併後就是 0x0200020207000000。清單中包含 SSSE3,是因為 OpenSSL 的 PCLMULQDQ based GHASH 會使用 pshufb 交換位元組,而 generic guest CPU model 會在隱藏 AES-NI 的同時一併隱藏 SSSE3。

這裡有兩項警告,而且你可以刻意觸發這兩種情況。

只設定第一個 vector,會讓後續 vectors 保持為零,因而關閉 AVX2 與 AVX-512 code paths。這裡是刻意如此設定。不要嘗試在被遮罩的 guest 上強制啟用 AVX bits,因為 AVX registers 需要作業系統在 XCR0 中啟用 extended state,而你的 kernel 會根據相同的 masked CPUID 判定不啟用。之後執行 VEX-encoded instruction 時會引發 undefined-opcode fault,導致 process 終止。

在實際不具備 AES-NI 的 core 上強制啟用 AES-NI,會立即終止 process:

Illegal instruction (core dumped)

這是 AESENC 引發 undefined-opcode fault,因為該 core 沒有可供執行的這項指令。部分 server firmware 也能在下一次 reset 前,於硬體中停用 AES-NI,症狀完全相同。無論是哪種情況,解決方法都是更換 host,而不是更換 environment variable。

若要讓長時間執行的 service 持續使用這項 override,請使用 systemd drop-in。

sudo systemctl edit nginx
[Service]
Environment="OPENSSL_ia32cap=0x0200020207000000"
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl show nginx -p Environment

最後一個 command 應該會將該 variable 輸出回來。請了解這項設定的代價:如果該 server 日後遷移到實際不具備 AES-NI 的 host,nginx 會在第一個 TLS connection 上因 illegal instruction 而終止。請將這項風險記錄在 runbook 中;或者完全不要在 production 使用 override,只在開立 ticket 時用它驗證問題。

override 無法修正的部分

OPENSSL_ia32cap 只會影響 OpenSSL,其他軟體都不會受到影響。每個軟體都會自行偵測功能,不會讀取這個變數。

核心是重要的例外。dm-crypt 和 LUKS 使用核心加密 API;CPU 缺少功能位元時,aesni_intel 模組會拒絕載入:

modprobe: ERROR: could not insert 'aesni_intel': No such device

這沒有對應的使用者空間變數。核心會在開機時讀取一次 CPUID;除非重新開機並改用其他主機,否則該決定會持續有效。因此,無論 OpenSSL 如何運作,加密磁碟區都會持續使用軟體密碼。請測量實際使用的效能:

sudo cryptsetup benchmark -c aes-xts -s 256

硬體支援 AES 時,aes-xts 256b 列的速度可達數千 MiB/s;沒有硬體支援時,速度則只有數百 MiB/s 左右。具備自行偵測機制的語言執行環境也同樣不受影響,Go 和 Java 都是其中的例子。Go 的 crypto/aes 會直接檢查 CPUID;功能位元未設定時,會靜默改用其常數時間軟體實作。如果負責終止 TLS 的服務是 Go 二進位檔,OpenSSL 變數對它不會產生任何作用。

若無法使用 AES-NI,請優先選擇 ChaCha20

ChaCha20-Poly1305 的設計目標是在純軟體環境中維持高速。在無法使用 AES-NI 的核心上,它通常大幅優於 AES-GCM。因此,在這類主機上,合理的做法是不再優先選擇 AES。

適用於 nginx 1.19.4 及更新版本,且建置時使用 OpenSSL 1.1.1 或更新版本:

ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384;

ssl_ciphers涵蓋 TLS 1.2。ssl_conf_command Ciphersuites涵蓋 TLS 1.3;nginx 沒有專用指令,而是直接將字串傳給 OpenSSL,不會先驗證內容,因此其中的拼字錯誤會被靜默接受。重新載入設定,並確認用戶端收到的內容:

sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -i cipher

正常結果會列出 TLS_CHACHA20_POLY1305_SHA256。在套用變更前,請在同一台主機上執行 openssl speed -evp chacha20-poly1305,並與 AES 測試結果並列比較,再由兩個數值決定。

ARM 主機使用不同的擴充指令集

AES-NI 僅支援 x86。ARM VPS 使用 ARMv8 cryptographic extensions,這是執行相同功能的另一套指令集。在 aarch64 上,旗標位於 Features,而不是 flags

grep -m1 Features /proc/cpuinfo

請尋找 aespmullpmull 是 ARM 對應 PCLMULQDQ 的功能,GCM 基於相同原因需要它。OpenSSL 在 ARM 上使用的覆寫變數是 OPENSSL_armcap,其位元配置由 OpenSSL 原始碼中的 crypto/arm_arch.h 定義,因此本指南中的 x86 十六進位值在 ARM 上沒有意義。實務上,作為 VPS 主機銷售的 ARM 伺服器核心通常會公開這些擴充功能,因此遮罩功能問題主要出現在 x86 環境。

故障模式與你將看到的字串

/proc/cpuinfo 中沒有 aes,而且強制執行速度快得多。 主機正在遮蔽 CPUID。確認模型名稱是通用名稱,然後詢問供應商其 hypervisor 提供的 guest CPU 模型。

/proc/cpuinfo 中沒有 aes,而強制執行會輸出 Illegal instruction 這些指令確實不存在,或已由韌體停用。請將工作負載移至其他主機。

aes 存在,但吞吐量仍然偏低。 確認你讀取的是 8192-byte 欄位,且沒有其他工作負載使用該核心。在共享方案中,吵雜鄰居的影響看起來與缺少 CPU 功能完全相同,直到你在不同時段執行測試兩次為止。

遮蔽執行與正常執行得到相同數值。 OpenSSL 原本就採用 software path。這個結果就是觀察結果,不是測試錯誤。

巢狀虛擬化會在下一層改變結果。 guest 內的 guest 會取得中間層選擇傳遞的 CPUID,而 AES-NI 很容易在該層遺失,且不易察覺。如果你在 VPS 上執行巢狀虛擬機器,也請在內部 guest 中檢查該旗標,以及在你租用的機器上檢查。

FAQ

為什麼我的 VPS 在 /proc/cpuinfo 中沒有 aes 旗標?

因為 hypervisor 提供的是通用 guest CPU model。qemu64kvm64 的功能集合不包含 AES-NI,因此無論實體處理器為何,CPUID 都會回報該功能不存在。主機這麼做,是為了讓執行中的 guest 能在不同 CPU 的機器之間遷移。執行 grep -m1 'model name' /proc/cpuinfo:若顯示類似 QEMU Virtual CPU version 2.5+Common KVM processor 的字串,就是判斷依據;若顯示真正的 Xeon 或 EPYC model string,表示 CPU model 已透傳,而該旗標確實不存在於處理器硬體中。

OPENSSL_ia32cap 真的會啟用 AES-NI,還是只是假裝啟用?

它會啟用真正的指令。AES-NI 指令不需要特權,hypervisor 也不會攔截,因此無論 CPUID 回報什麼,AESENC 都會原生執行。只有 CPUID 指令會遭到攔截。將 OPENSSL_ia32cap 設為單純的十六進位值,會取代 OpenSSL 從 CPUID 取得的回應,因此 OpenSSL 會選擇硬體程式碼路徑,接著由硬體以完整速度執行。若處理器硬體確實不支援這些指令,程序會在第一次 AES 操作時因 Illegal instruction (core dumped) 而終止。

這項覆寫設定會加速我的 LUKS 加密磁碟區嗎?

不會。OPENSSL_ia32cap 只會由 OpenSSL 讀取,其他元件不會讀取。LUKS 和 dm-crypt 使用 kernel crypto API;當功能位元未設定時,aesni_intel module 會因 modprobe: ERROR: could not insert 'aesni_intel': No such device 而載入失敗。kernel 會在開機時讀取 CPUID,user space 變數無法改變這項結果。使用 sudo cryptsetup benchmark -c aes-xts -s 256 測量實際數值,再將 aes-xts 256b 列與會回報該旗標的主機比較。

缺少 AES-NI 旗標會降低 WireGuard 的速度嗎?

不會。WireGuard 的所有資料都使用 ChaCha20-Poly1305,從不使用 AES 指令,因此在功能被遮蔽與未被遮蔽的主機上,吞吐量相同。OpenVPN 和使用 AES-GCM 設定的 IPsec,若主機沒有 AES-NI,吞吐量確實會下降。因此,同一個 VPS 上的兩條 tunnel 可能有非常不同的表現;在將問題歸咎於網路前,應先了解這點。

如何在 ARM VPS 上檢查 AES-NI?

ARM core 沒有 AES-NI。它們具備 ARMv8 cryptographic extensions,使用不同指令完成相同工作。執行 grep -m1 Features /proc/cpuinfo,並尋找 aespmull;aarch64 會將這些功能列在 Features 下,而不是 flags。x86 的 OPENSSL_ia32cap 值在 ARM 上沒有意義。OpenSSL 在該平台上的對應變數是 OPENSSL_armcap,其位元配置定義於 OpenSSL 原始碼中的 crypto/arm_arch.h