การสร้างใบรับรองที่ลงชื่อด้วยตัวเองด้วย openssl ที่ทำงานใน Chrome 58


52

ตั้งแต่ Chrome 58 จะไม่ยอมรับ certs ที่ลงชื่อด้วยตนเองซึ่งพึ่งพาCommon Name: https://productforums.google.com/forum/#!topic/chrome/zVo3M8CgKzQ;context-place=topicsearchin/chrome/category $ 3ACanary% 7Csort อีกต่อไป: ความสัมพันธ์กัน% 7Cspell: เท็จ

Subject Alt Nameแต่มันต้องใช้ ก่อนหน้านี้ฉันได้ปฏิบัติตามคำแนะนำเกี่ยวกับวิธีสร้างใบรับรองที่ลงชื่อด้วยตนเอง: https://devcenter.heroku.com/articles/ssl-certificate-selfซึ่งใช้งานได้ดีมากเพราะฉันต้องการserver.crtและserver.keyไฟล์สำหรับสิ่งที่ฉันทำ ตอนนี้ฉันต้องสร้าง certs ใหม่ที่รวมถึงSANความพยายามทั้งหมดของฉันที่ทำไม่ได้กับ Chrome 58

นี่คือสิ่งที่ฉันทำ:

ฉันทำตามขั้นตอนในบทความ Heroku ที่กล่าวถึงข้างต้นเพื่อสร้างรหัส ฉันเขียนไฟล์กำหนดค่า OpenSSL ใหม่:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
req_extensions      = san
extensions          = san
[ req_distinguished_name ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com

จากนั้นสร้างserver.crtคำสั่งด้วยคำสั่งต่อไปนี้:

openssl req \
-new \
-key server.key \
-out server.csr \
-config config.cnf \
-sha256 \
-days 3650

ฉันใช้ Mac ดังนั้นฉันจึงเปิดserver.crtไฟล์ด้วย Keychain เพิ่มไว้ในใบรับรองระบบของฉัน Always Trustจากนั้นผมก็ตั้งค่าให้

ด้วยข้อยกเว้นของไฟล์ปรับแต่งเพื่อตั้งค่า SAN สิ่งเหล่านี้เป็นขั้นตอนที่คล้ายกันที่ฉันใช้ใน Chrome เวอร์ชันก่อนหน้าเพื่อสร้างและเชื่อถือใบรับรองที่ลงนามด้วยตนเอง

อย่างไรก็ตามหลังจากนี้ฉันยังได้รับERR_CERT_COMMON_NAME_INVALIDใน Chrome 58

คำตอบ:


61

ทางออกของฉัน:

openssl req \
    -newkey rsa:2048 \
    -x509 \
    -nodes \
    -keyout server.key \
    -new \
    -out server.crt \
    -subj /CN=dev.mycompany.com \
    -reqexts SAN \
    -extensions SAN \
    -config <(cat /System/Library/OpenSSL/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')) \
    -sha256 \
    -days 3650

สถานะ: ใช้งานได้สำหรับฉัน


2
การใช้งานที่ดีของ subshell ฉันคิดว่าคุณสามารถทำให้มันง่ายขึ้นเล็กน้อย:-config <(cat /System/Library/OpenSSL/openssl.cnf ; printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')
jrwren

1
ฉันไม่ได้รับข้อผิดพลาดชื่อ Alt เรื่องอีกต่อไป แต่ตอนนี้ฉันได้รับข้อผิดพลาดเกี่ยวกับชื่อสามัญและการตั้งค่าใบรับรองที่ดาวน์โหลดเป็น "always trust" ไม่ทำงาน ความคิดใด ๆ @bcardarella
rugbert

2
เมื่ออัปเดตเป็น Chrome 59 ใบรับรองจะแสดงข้อผิดพลาดดังนี้: มีปัญหากับห่วงโซ่ใบรับรองของเว็บไซต์ (สุทธิ :: ERR_CERT_COMMON_NAME_INVALID)
theHarsh

1
ฉันเปลี่ยนdev.company.nameเป็นlocalhostและสิ่งนี้ใช้สำหรับการให้บริการเว็บไซต์การพัฒนาท้องถิ่นจาก localhost ใน macOS ฉันต้องเพิ่มใบรับรองไปที่ Keychain และตั้งค่า SSL เป็น "Always Trust"
แดเนียลเอ็ม

1
นั่นเป็นวิธีที่ง่ายที่สุดและไม่ต้องใช้การหมุนด้วย sslconf หรือติดตั้ง CA
bp

16

ใน Windows ให้บันทึกสคริปต์นี้ในโฟลเดอร์ SSL ของคุณเป็น makeCERT.bat มันจะสร้างไฟล์เหล่านี้: example.cnf, example.crt, example.key

@echo off

REM IN YOUR SSL FOLDER, SAVE THIS FILE AS: makeCERT.bat
REM AT COMMAND LINE IN YOUR SSL FOLDER, RUN: makecert
REM IT WILL CREATE THESE FILES: example.cnf, example.crt, example.key
REM IMPORT THE .crt FILE INTO CHROME Trusted Root Certification Authorities
REM REMEMBER TO RESTART APACHE OR NGINX AFTER YOU CONFIGURE FOR THESE FILES

REM PLEASE UPDATE THE FOLLOWING VARIABLES FOR YOUR NEEDS.
SET HOSTNAME=example
SET DOT=com
SET COUNTRY=US
SET STATE=KS
SET CITY=Olathe
SET ORGANIZATION=IT
SET ORGANIZATION_UNIT=IT Department
SET EMAIL=webmaster@%HOSTNAME%.%DOT%

(
echo [req]
echo default_bits = 2048
echo prompt = no
echo default_md = sha256
echo x509_extensions = v3_req
echo distinguished_name = dn
echo:
echo [dn]
echo C = %COUNTRY%
echo ST = %STATE%
echo L = %CITY%
echo O = %ORGANIZATION%
echo OU = %ORGANIZATION_UNIT%
echo emailAddress = %EMAIL%
echo CN = %HOSTNAME%.%DOT%
echo:
echo [v3_req]
echo subjectAltName = @alt_names
echo:
echo [alt_names]
echo DNS.1 = *.%HOSTNAME%.%DOT%
echo DNS.2 = %HOSTNAME%.%DOT%
)>%HOSTNAME%.cnf

openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout %HOSTNAME%.key -days 3560 -out %HOSTNAME%.crt -config %HOSTNAME%.cnf

13

นี่เป็นวิธีแก้ปัญหาที่เหมาะกับฉัน:

สร้างคีย์ CA และใบรับรอง

# openssl genrsa -out server_rootCA.key 2048
# openssl req -x509 -new -nodes -key server_rootCA.key -sha256 -days 3650 -out server_rootCA.pem

สร้าง server_rootCA.csr.cnf

# server_rootCA.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=DE
ST=Berlin
L=NeuKoelln
O=Weisestrasse
OU=local_RootCA
emailAddress=ikke@server.berlin
CN = server.berlin

สร้างไฟล์คอนฟิกูเรชัน v3.ext

# v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = server.berlin

สร้างรหัสเซิร์ฟเวอร์

# openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server_rootCA.csr.cnf )

สร้างใบรับรองเซิร์ฟเวอร์

# openssl x509 -req -in server.csr -CA server_rootCA.pem -CAkey server_rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile v3.ext

เพิ่มใบรับรองและรหัสไปยังส่วนไซต์ไฟล์ Apache2, HTTPS (พอร์ต 443)

SSLCertificateFile    /etc/apache2/ssl/server.crt
SSLCertificateKeyFile    /etc/apache2/ssl/server.key

คัดลอก server_rootCA.pem จากเซิร์ฟเวอร์ไปยังเครื่องของคุณ

# scp you@server.berlin:~/server_rootCA.pem .

.. และเพิ่มลงในเบราว์เซอร์ Chromium

Chromium -> Setting -> (Advanced) Manage Certificates -> Import -> 'server_rootCA.pem'

คุณทำเสร็จแล้ว!

PSแทนที่จะสร้างคู่ CA และเซิร์ฟเวอร์รับรองการทำงาน (ตามคำแนะนำด้านบน) คุณสามารถปิดการใช้งานส่วนหัว HSTS ในการกำหนดค่าเซิร์ฟเวอร์ HTTP ของคุณ วิธีนี้จะป้องกันไม่ให้ Chromium บังคับใช้ HTTPS และจะอนุญาตให้ผู้ใช้คลิก“ ขั้นสูง→ไปที่ your.url (ไม่ปลอดภัย)” โดยไม่ต้องรับและติดตั้งใบรับรอง CA (server_rootCA.pem) ที่กำหนดเองของคุณ กล่าวอีกนัยหนึ่ง - การปิดใช้งาน HSTS จะช่วยให้เว็บไซต์ของคุณสามารถดูต่อสาธารณะผ่าน HTTP และ / หรือการเชื่อมต่อ HTTPS ที่ไม่ปลอดภัย (ระวัง!)

สำหรับ Apache2 เพิ่มส่วนต่อไปนี้ไปยังไฟล์ไซต์ส่วน HTTP (พอร์ต 80)

Header unset Strict-Transport-Security
Header always set Strict-Transport-Security "max-age=0;includeSubDomains"

ทดสอบกับ Debian / Apache2.4 + Debian / Chromium 59

https://ram.k0a1a.net/self-signed_https_cert_after_chrome_58


การไปตามเส้นทางของผู้มีอำนาจของรูต CA ซึ่งต่อมาลงชื่อแต่ละ certs เป็นวิธีเดียวที่ฉันจะได้รับ chrome เพื่อตรวจสอบสิทธิ์อย่างเต็มที่ ยังมีข้อได้เปรียบที่ฉันต้องการเพียงแค่ให้คนติดตั้งใบรับรองเดียว ขอบคุณ
geoff

4
มีคนช่วยอธิบายให้ฉันหน่อยได้ไหมว่าทำไมทุกคนในพื้นที่นี้ดูเหมือนจะใช้วิธีทุบตีแบบนี้-config <( cat server_rootCA.csr.cnf )แทนที่จะเป็นแค่-config server_rootCA.csr.cnf?
Caesar

คุณช่วยกรุณาอัปเดตคำตอบของคุณที่เกี่ยวข้องกับส่วนหัวของ apache ที่สามารถข้ามปัญหาได้ (ฉันไม่คิดว่านี่เป็นเว็บไซต์ในท้องถิ่นเพื่อการพัฒนาเท่านั้นและฉันจะแก้ไขปัญหาทั่วไปโดยไม่ต้องสร้างใบรับรองใหม่ในแต่ละครั้ง) คุณช่วยชี้ให้เห็นว่าสิ่งเหล่านี้ควรไปที่ไหนในการโฮสต์เสมือน ฉันได้ลองตัวเลือกหลายตัวแล้วและยังไม่สามารถเข้าถึงเว็บไซต์ได้ thorugh https ขอบคุณ
Nikos M.

12

มีคำตอบที่ดีมากมายที่ให้ตัวอย่างของวิธีการทำงานนี้ แต่ไม่มีใครอธิบายว่าสิ่งใดที่ผิดพลาดในความพยายามของคุณ OpenSSL นั้นค่อนข้างใช้งานไม่ได้ในบางครั้งดังนั้นจึงควรค่าแก่การเดินผ่าน

ก่อนอื่น OpenSSL จะใช้ค่าเริ่มต้นเพื่อข้ามค่าชื่อที่แตกต่างที่คุณระบุในการกำหนดค่า หากคุณต้องการใช้คุณต้องเพิ่มprompt = no การกำหนดค่าของคุณ นอกจากนี้คำสั่งที่เขียนจะสร้างการร้องขอใบรับรอง ไม่ใช่ใบรับรองเองดังนั้น-daysคำสั่งจะไม่ทำอะไรเลย

หากคุณสร้างคำขอใบรับรองโดยใช้คำสั่งนี้คุณให้และตรวจสอบผลลัพธ์ชื่อ Alt ของหัวเรื่องจะปรากฏขึ้น:

$ openssl req -new -key server.key -out server.csr -config config.cnf -sha256
$ openssl req -text -noout -in server.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:dev.mycompany.com
    Signature Algorithm: sha256WithRSAEncryption
         ...

แต่ถ้าคุณสร้างใบรับรองโดยใช้คำสั่งในลิงค์ heroku และตรวจสอบผลลัพธ์ชื่อ Alt ของหัวเรื่องจะหายไป:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
$ openssl x509 -text -noout -in server.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            89:fd:75:26:43:08:04:61
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Validity
            Not Before: Jan 21 04:27:21 2018 GMT
            Not After : Jan 21 04:27:21 2019 GMT
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

เหตุผลก็คือโดยค่าเริ่มต้น OpenSSL จะไม่คัดลอกส่วนขยายจากคำขอไปยังใบรับรอง โดยปกติแล้วใบรับรองจะถูกสร้าง / เซ็นชื่อโดย CA ตามคำขอจากลูกค้าและส่วนขยายบางอย่างสามารถให้ใบรับรองพลังงานมากกว่า CA นั้นตั้งใจหากพวกเขาเชื่อถือส่วนขยายที่กำหนดไว้ในคำขอโดยไม่ตั้งใจ

มีวิธีที่จะบอก OpenSSL เพื่อคัดลอกส่วนขยาย แต่ IMHO มันทำงานได้ดีกว่าเพียงแค่ให้ส่วนขยายในไฟล์กำหนดค่าเมื่อคุณสร้างใบรับรอง

หากคุณพยายามที่จะใช้ไฟล์ปรับแต่งที่มีอยู่ของคุณมันจะไม่ทำงานเพราะส่วนระดับบนสุดถูกทำเครื่องหมาย[req]ดังนั้นการตั้งค่าเหล่านั้นจะใช้กับคำสั่ง req ไม่ใช่คำสั่ง x509 เท่านั้น ไม่จำเป็นต้องมีตัวทำเครื่องหมายส่วนระดับบนสุดดังนั้นคุณสามารถลบบรรทัดแรกนั้นออกมาและมันจะทำงานได้ดีสำหรับการสร้างคำขอหรือใบรับรอง

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt -extfile config.cnf

อีกวิธีหนึ่งคุณสามารถใช้-x509อาร์กิวเมนต์ไปยังreqคำสั่งเพื่อสร้างใบรับรองแบบลงนามด้วยตนเองในคำสั่งเดียวแทนที่จะสร้างคำขอก่อนจากนั้นจึงสร้างใบรับรอง ในกรณีนี้ไม่จำเป็นต้องลบ [req]บรรทัดส่วนเนื่องจากส่วนนั้นถูกอ่านและใช้โดยคำสั่ง req

$ openssl req -x509 -sha256 -days 365 -key server.key -out server.crt -config config.cnf

ในการปะยางนี่คือไฟล์ปรับแต่งที่ใช้ในคำสั่งด้านบน:

default_bits        = 2048
distinguished_name  = dn
x509_extensions     = san
req_extensions      = san
extensions          = san
prompt              = no
[ dn ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com

2
นี่เป็นคำอธิบายเดียวที่ช่วยให้ฉันเข้าใจว่าทำไมใบรับรองออกมาโดยไม่มี SAN (ในกรณีของฉันฉันจำเป็นต้องรวม x509_extensions ในไฟล์ปรับแต่ง)
Daniel Beardsmore

2

โซลูชันของฉันคือการรักษาหลักไว้openssl.cnfเหมือนเดิมและในตอนท้ายเพื่อเพิ่มหัวข้อใหม่เช่น[ cert_www.example.com ]ที่ www.example.com เป็นเว็บไซต์ที่ฉันต้องการสร้างใบรับรองและในนั้นให้ใส่สิ่งที่subjectAltNameฉันต้องการ (และ สิ่งอื่นใด) แน่นอนว่าส่วนนั้นสามารถตั้งชื่อตามที่คุณต้องการ

หลังจากนั้นฉันสามารถเรียกใช้openssl reqคำสั่งเหมือนเดิมเพียงเพิ่ม-extensions cert_www.example.comสำหรับเนื้อหาที่จะหยิบขึ้นมาและฉันเพิ่ม-subjเพื่อเพิ่มข้อมูล DN ทั้งหมดโดยตรง

อย่าลืมตรวจสอบเนื้อหาใบรับรองหลังจากการสร้างและก่อนใช้ด้วย openssl x509 -text


1

Bash script ที่มีการกำหนดค่าอบ

ในฐานะเชลล์สคริปต์ที่ควรทำงานข้ามแพลตฟอร์มด้วย bash สมมติว่าHOSTNAMEenv ถูกตั้งค่าสำหรับเชลล์หรือระบุชื่อโฮสต์ที่คุณเลือกเช่นself_signed_cert.sh test

set -e

if [ -z "$1" ]; then
  hostname="$HOSTNAME"
else
  hostname="$1"
fi

local_openssl_config="
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = san_self_signed
[ req_distinguished_name ]
CN=$hostname
[ san_self_signed ]
subjectAltName = DNS:$hostname, DNS:localhost
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth, timeStamping
"

openssl req \
  -newkey rsa:2048 -nodes \
  -keyout "$hostname.key.pem" \
  -x509 -sha256 -days 3650 \
  -config <(echo "$local_openssl_config") \
  -out "$hostname.cert.pem"
openssl x509 -noout -text -in "$hostname.cert.pem"

ข้างต้นมากขึ้นหรือน้อยลง injects ความต้องการ openssl ไฟล์ข้อมูลขั้นต่ำเปลือย

หมายเหตุรวมพิเศษDNS:localhostเป็น SAN เพื่อให้การทดสอบผ่าน localhost ง่ายขึ้น ลบบิตพิเศษนั้นออกจากสคริปต์หากคุณไม่ต้องการ

เครดิต

คำตอบของ bcardarellaนั้นยอดเยี่ยม (ไม่สามารถแสดงความคิดเห็น / โหวตได้เนื่องจากตัวแทนไม่เพียงพอ) อย่างไรก็ตามคำตอบนั้นใช้ตำแหน่งไฟล์ configslsl ที่มีอยู่ซึ่งเป็นแพลตฟอร์มเฉพาะ ... ด้วยเหตุนี้:

ได้ผลสำหรับฉัน

เห็นได้ชัดว่าเราจะต้องค้นหาไฟล์ openssl สำหรับแพลตฟอร์มที่คุณกำหนดและแทนที่ตำแหน่งที่ถูกต้อง

ทดสอบ

สำหรับวิธีทดสอบให้นำเข้าtest.cert.pemสู่หน่วยงานของ chrome ในchrome://settings/certificatesและ:

openssl s_server -key test.key.pem -cert test.cert.pem -accept 20443 -www &
openssl_pid=$!
google-chrome https://localhost:20443

และหลังการทดสอบ

kill $openssl_pid
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.