콘텐츠로 이동
무료 평가 시작

PKI와 신뢰 설정

  • 서버와 클라이언트의 응용 프로그램 인스턴스 인증서(application instance certificate)와 개인 키를 만듭니다.
  • 인증서에 필요한 확장(extension)이 들어갔는지 검사합니다.
  • 상대 인증서를 신뢰할 때와 신뢰하지 않을 때 연결이 어떻게 되는지 확인합니다.
  • 실습 환경의 1~3단계: 패키지를 풀고 환경 변수를 설정합니다.
  • OpenSSL 3 명령행 도구

SDK의 examples/opcua/pki에는 OpenSSL 설정 템플릿이 두 개 있습니다.

examples/opcua/pki/server.cnf
# Self-signed OPC UA application instance certificate for the example server.
# Change the URI to your server ApplicationUri and the DNS/IP names to the host
# names used in the endpoint URL.
[req]
prompt = no
distinguished_name = subject
x509_extensions = application
[subject]
CN = PDComm OPC UA Example Server
O = Example Organization
[application]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = URI:urn:pdcomm:opcua:server, DNS:localhost, IP:127.0.0.1
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
examples/opcua/pki/client.cnf
# Self-signed OPC UA application instance certificate for the example client.
# The URI must equal the client ApplicationUri (default urn:pdcomm:opcua:client).
[req]
prompt = no
distinguished_name = subject
x509_extensions = application
[subject]
CN = PDComm OPC UA Example Client
O = Example Organization
[application]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = URI:urn:pdcomm:opcua:client, DNS:localhost
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
설정 의미
CN, O 주체 이름. 비어 있으면 PDComm이 인증서를 거부합니다.
basicConstraints = critical, CA:FALSE 응용 프로그램 인증서이며 인증 기관(CA)이 아닙니다.
keyUsage OPC UA가 요구하는 키 용도. 자체 서명이므로 keyCertSign을 포함합니다.
extendedKeyUsage 서버 인증(serverAuth)과 클라이언트 인증(clientAuth)
subjectAltName URI:는 ApplicationUri, DNS:와 IP:는 이 서버에 접속할 때 쓰는 호스트 이름과 주소

2. 서버 인증서의 호스트 이름 정하기

섹션 제목: “2. 서버 인증서의 호스트 이름 정하기”

클라이언트는 endpoint URL의 호스트가 서버 인증서의 DNS: 또는 IP: 항목과 일치하는지 검사합니다. 템플릿은 localhost와 127.0.0.1만 담고 있습니다. 다른 컴퓨터나 컴퓨터 이름으로 접속할 계획이면 그 이름을 추가합니다.

컴퓨터 이름을 확인합니다.

터미널 창
hostname

$PKI/server.cnf의 subjectAltName 줄에 이름을 추가합니다. 이 튜토리얼에서는 컴퓨터 이름을 my-pc라고 가정합니다. 실제 이름으로 바꿉니다.

subjectAltName = URI:urn:pdcomm:opcua:server, DNS:localhost, DNS:my-pc, IP:127.0.0.1

일부 Windows 클라이언트는 endpoint URL의 localhost를 컴퓨터 이름으로 바꿔 접속합니다. 서버가 Windows에서 실행되거나 Windows 클라이언트가 접속한다면 컴퓨터 이름을 꼭 넣습니다. 자세한 내용은 인증서 문제에서 설명합니다.

터미널 창
cd "$PKI"
openssl req -x509 -new -config server.cnf -newkey rsa:2048 -sha256 -days 365 -noenc \
-keyout server.key.pem -out server.cert.pem
openssl x509 -in server.cert.pem -outform DER -out server.der
openssl req -x509 -new -config client.cnf -newkey rsa:2048 -sha256 -days 365 -noenc \
-keyout client.key.pem -out client.cert.pem
openssl x509 -in client.cert.pem -outform DER -out client.der
chmod 600 server.key.pem client.key.pem
ls -1

결과:

client.cert.pem
client.cnf
client.der
client.key.pem
server.cert.pem
server.cnf
server.der
server.key.pem

파일의 쓰임은 다음과 같습니다.

파일 형식 누가 쓰나
server.der DER 인증서 서버의 신원. 클라이언트가 신뢰할 인증서로도 지정합니다.
server.key.pem PEM 개인 키 서버만 읽습니다.
server.cert.pem PEM 인증서 시뮬레이터 서버의 신원 파일로 씁니다.
client.der DER 인증서 클라이언트의 신원. 서버가 신뢰할 인증서로도 지정합니다.
client.key.pem PEM 개인 키 클라이언트만 읽습니다.
client.cert.pem PEM 인증서 시뮬레이터 클라이언트의 신원 파일로 씁니다.

SDK는 인증서를 DER로, 개인 키를 PEM으로 받습니다. 각 파일은 64 KiB 이하여야 합니다. 시뮬레이터는 DER과 PEM을 모두 읽습니다.

서버 인증서의 주체와 확장을 확인합니다.

터미널 창
openssl x509 -in server.cert.pem -noout -subject -ext subjectAltName,keyUsage,extendedKeyUsage
subject=CN=PDComm OPC UA Example Server, O=Example Organization
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Certificate Sign
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Alternative Name:
URI:urn:pdcomm:opcua:server, DNS:localhost, DNS:my-pc, IP Address:127.0.0.1

유효 기간도 확인합니다. 템플릿 명령은 1년짜리 인증서를 만듭니다.

터미널 창
openssl x509 -in server.cert.pem -noout -dates
notBefore=Oct 14 16:56:03 2026 GMT
notAfter=Oct 14 16:56:03 2027 GMT

예제를 빌드하지 않았다면 실습 환경의 5단계를 먼저 실행합니다. 첫 번째 터미널에서 서버를 실행합니다. 서버는 client.der만 신뢰합니다.

터미널 창
cd "$OPCUA_SDK"
build/opcua_server 127.0.0.1:4840 opc.tcp://localhost:4840/pdcomm "$PKI/server.der" "$PKI/server.key.pem" "$PKI/client.der"

두 번째 터미널에서 클라이언트를 실행합니다. 클라이언트는 server.der만 신뢰합니다.

터미널 창
cd "$OPCUA_SDK"
build/opcua_client opc.tcp://localhost:4840/pdcomm "$PKI/client.der" "$PKI/client.key.pem" "$PKI/server.der"

엔드포인트 목록 뒤에 다음 줄이 나오면 서로를 신뢰한 것입니다.

Session activated (anonymous)
Read ns=2;i=1001 value=42 service=0x00000000 status=0x00000000
Session closed

URL의 호스트를 127.0.0.1로 바꿔도 연결됩니다. 서버 인증서에 IP:127.0.0.1이 있기 때문입니다.

신뢰하지 않는 인증서를 하나 더 만듭니다.

터미널 창
cd "$PKI"
openssl req -x509 -new -config client.cnf -newkey rsa:2048 -sha256 -days 365 -noenc \
-keyout other.key.pem -out other.cert.pem
openssl x509 -in other.cert.pem -outform DER -out other.der
chmod 600 other.key.pem
cd "$OPCUA_SDK"

클라이언트가 서버를 신뢰하지 않을 때. 신뢰할 서버 인증서로 other.der를 지정합니다.

터미널 창
build/opcua_client opc.tcp://localhost:4840/pdcomm "$PKI/client.der" "$PKI/client.key.pem" "$PKI/other.der"
Error: connect failed (SDK status 5): OPC UA security CertificateUntrusted

클라이언트가 서버 인증서를 검증하다 거부했으므로 오류 이름이 그대로 보입니다. 종료 코드는 3입니다.

서버가 클라이언트를 신뢰하지 않을 때. 클라이언트 신원으로 other.der와 other.key.pem을 씁니다.

터미널 창
build/opcua_client opc.tcp://localhost:4840/pdcomm "$PKI/other.der" "$PKI/other.key.pem" "$PKI/server.der"
Error: connect failed (SDK status 5): OPC UA status: 0x80130000

0x80130000은 BadSecurityChecksFailed입니다. 서버는 보안을 위해 거부 이유를 자세히 알려 주지 않습니다. 이 오류가 나면 서버 쪽 신뢰 설정을 먼저 확인합니다.

인증서와 개인 키가 짝이 맞지 않을 때.

터미널 창
build/opcua_client opc.tcp://localhost:4840/pdcomm "$PKI/client.der" "$PKI/other.key.pem" "$PKI/server.der"
Error: connect failed (SDK status 5): OPC UA security InvalidKey
  • 개인 키 파일은 실행 계정만 읽게 합니다(chmod 600).
  • 개인 키를 패키지, 저장소, 로그에 넣지 않습니다. PDComm SDK와 예제는 개인 키와 암호를 출력하지 않습니다.
  • SDK는 연결할 때 키를 복사합니다. 연결 직후 응용 프로그램의 키 버퍼를 지웁니다. 예제의 example_wipe가 그 예입니다.
  • 인증서가 만료되기 전에 새로 만들고, 상대 쪽의 신뢰 설정도 새 인증서로 바꿉니다.

서버 터미널에서 Ctrl+C를 누릅니다. 시험용 인증서를 지웁니다.

터미널 창
rm "$PKI"/other.*

server.*와 client.*는 다른 튜토리얼에서 계속 씁니다.