보안이 지원되는 OPC UA 클라이언트 만들기
OPC UA의 핵심 기능 중 하나는 보안 지원으로, 암호화된 암호화 및 서명 프로토콜, 사용자 인증 및 권한 부여를 지원한다는 의미입니다.
이를 위해서는 각 애플리케이션 인스턴스(프로그램 설치)에 고유한 Application Instance Certificate
및 해당 개인키가 있어야 합니다.
애플리케이션은 자체적으로 자체 서명된 인증서를 생성하거나 OPC UA GDS를 사용하여 인증 기관에서 인증서를 받거나 사용자가 수동으로 생성한 인증서로 간단히 구성할 수 있습니다.
현재 Qt OPC UA 는 인증서 생성 또는 GDS를 지원하지 않으므로 이 튜토리얼에서는 OpenSSL을 사용하여 명령줄에서 자체 서명된 OPC UA 인증서를 생성하는 방법을 설명합니다.
새 애플리케이션 인증서 만들기
OPC UA에 필요한 모든 확장을 포함하는 올바른 x509v3 인증서를 생성하려면 먼저 필요한 모든 정보가 포함된 구성 파일을 설정해야 합니다.
subject
및 subjectAltName
을 사례에 맞게 변경하는 것을 잊지 마세요.
subjectAltName
의 URI
필드에 애플리케이션의 ApplicationURI
을 삽입하고 DNS
의 subjectAltName
필드에 PC 또는 디바이스의 호스트 이름을 삽입하는 것이 중요합니다. 또는 디바이스가 호스트 이름을 지원하지 않고 고정 IP로 작업하는 경우 IP
필드를 사용할 수 있습니다. 향후 Qt OPC UA 버전에서는 올바른 정보로 인증서를 생성할 수 있습니다. 현재로서는 OpenSSL 명령줄 도구를 사용하여 인증서를 만들 수 있습니다.
예시: opcuaviewer.config
[ req ] default_bits = 2048 default_md = sha256 distinguished_name = subject req_extensions = req_ext x509_extensions = req_ext string_mask = utf8only prompt = no [ req_ext ] basicConstraints = critical, CA:FALSE keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment extendedKeyUsage = clientAuth subjectAltName = URI:urn:foo.com:The%20Qt%20Company:QtOpcUaViewer,DNS:foo.com subjectKeyIdentifier = hash authorityKeyIdentifier=keyid:always,issuer:always [ subject ] countryName = DE stateOrProvinceName = Berlin localityName = Berlin organizationName = The Qt Company commonName = QtOpcUaViewer
이 구성 파일을 사용하여 OpenSSL은 로컬에서 사용할 수 있는 일치하는 인증서를 만들 수 있습니다.
# create a self-signed certificate and private key openssl req -new -x509 -config opcuaviewer.config -newkey rsa:2048 -keyout opcuaviewer.key -nodes -outform der -out opcuaviewer.der # install the certificate and key into the application PKI directory mv opcuaviewer.der /path/to/application/pki/own/certs/opcuaviewer.der mv opcuaviewer.key /path/to/application/pki/own/private/opcuaviewer.pem # secure private key file permissions chmod 600 /path/to/application/pki/own/private/opcuaviewer.pem
개인 키의 파일 권한을 보호하여 UA 애플리케이션만 읽을 수 있도록 하는 것이 중요합니다. 서비스(데몬)의 경우 이를 위한 전용 권한 없는 사용자 계정을 만들고 이 사용자를 키의 소유자로 만드는 것이 좋습니다. 대화형 애플리케이션의 경우 이 키는 사용자에게 개별적으로 부여해야 합니다. 대화형 애플리케이션의 경우 키를 비밀번호로 보호할 수도 있습니다. 이 경우 사용자는 애플리케이션을 시작하고 키를 로드할 때마다 비밀번호를 입력해야 합니다. 따라서 비밀번호로 보호된 키는 구성 파일에 비밀번호를 저장해야 하므로 무인 애플리케이션에 적합한 솔루션이 아닙니다.
OpenSSL을 사용하여 인증서 데이터를 덤프하여 인증서 내용을 검사할 수 있습니다:
openssl x509 -in opcuaviewer.der -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: be:aa:41:79:8a:b0:4f:9a Signature Algorithm: sha512WithRSAEncryption Issuer: C = DE, ST = Berlin, L = Berlin, O = The Qt Company, CN = QtOpcUaViewer Validity Not Before: Nov 7 14:38:52 2018 GMT Not After : Dec 7 14:38:52 2018 GMT Subject: C = DE, ST = Berlin, L = Berlin, O = The Qt Company, CN = QtOpcUaViewer Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: [ skipped ] Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Certificate Sign X509v3 Subject Alternative Name: URI:urn:foo.com:The%20Qt%20Company:QtOpcUaViewer, DNS:foo.com X509v3 Subject Key Identifier: B2:E8:5E:34:21:EA:67:CF:61:FC:14:94:18:C1:AD:13:89:83:CA:9B X509v3 Authority Key Identifier: keyid:B2:E8:5E:34:21:EA:67:CF:61:FC:14:94:18:C1:AD:13:89:83:CA:9B DirName:/C=DE/ST=Berlin/L=Berlin/O=The Qt Company/CN=QtOpcUaViewer serial:BE:AA:41:79:8A:B0:4F:9A Signature Algorithm: sha512WithRSAEncryption [ skipped ]
UA 애플리케이션 구성하기
이전 단계에서 만든 인증서로 보안을 작동하려면 다음을 수행해야 합니다.
- 올바른 애플리케이션 ID를 구성합니다
m_identity = m_pkiConfig.applicationIdentity();
- SDK가 인증서, 개인 키, 신뢰 목록 등을 찾을 수 있도록 PKI 위치를 구성합니다.
예를 들어 Qt OPC UA 뷰어 예제의 코드를 참조하세요:
void MainWindow::setupPkiConfiguration() { const QDir pkidir =QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/pki"); if (!pkidir.exists() && !copyDirRecursively(":/pki", pkidir.path())) qFatal("Could not set up directory %s!", qUtf8Printable(pkidir.path())); m_pkiConfig.setClientCertificateFile(pkidir.absoluteFilePath("own/certs/opcuaviewer.der")); m_pkiConfig.setPrivateKeyFile(pkidir.절대파일경로("own/private/opcuaviewer.pem")); m_pkiConfig.setTrustListDirectory(pkidir.absoluteFilePath("trusted/certs")); m_pkiConfig.setRevocationListDirectory(pkidir.absoluteFilePath("trusted/crl")); m_pkiConfig.setIssuerListDirectory(pkidir.absoluteFilePath("issuers/certs")); m_pkiConfig.setIssuerRevocationListDirectory(pkidir.absoluteFilePath("issuers/crl")); const QStringList toCreate = { m_pkiConfig.issuerListDirectory(),m_pkiConfig.issuerRevocationListDirectory() }; for(const QString &dir: toCreate) { if (!QDir().mkpath(dir)) qFatal("Could not create directory %s!", qUtf8Printable(dir)); } }
이 예에서는 미리 구성된 자체 및 신뢰할 수 있는 인증서를 Qt 리소스 시스템에서 파일 시스템의 쓰기 가능한 위치로 추출합니다. 발급자(해지) 목록의 나머지 디렉터리는 수동으로 만듭니다.
PKI 폴더 레이아웃
Qt OPC UA 는 다음 폴더 레이아웃을 사용합니다:
폴더 | 설명 |
---|---|
own | 애플리케이션의 자체 인증서를 저장할 위치입니다. |
신뢰할 수 있는 | 신뢰할 수 있는 애플리케이션 인증서 또는 신뢰할 수 있는 CA 인증서 목록입니다. |
발급자 | 신뢰할 수 없지만 인증서의 서명을 확인하는 데 필요한 CA에 대한 인증서 목록입니다. |
거부됨 | 나중에 관리자가 신뢰할 수 있도록 애플리케이션이 거부된 인증서를 여기에 저장합니다. 여기에 최대 파일 수를 구성하는 것이 중요합니다. 여기에 도달하면 가장 오래된 파일부터 삭제해야 합니다. 제한이 없으면 공격자가 잘못된 연결 시도로 컴퓨터의 하드 디스크를 가득 채울 수 있습니다. |
own
, trusted
, issuers
폴더 각각에는 다음 표에 정의된 하위 디렉터리 구조가 포함되어 있습니다.
하위 디렉터리 | 설명 |
---|---|
certs | DER 인코딩된 X.509 v3 인증서를 포함합니다. 파일 확장자는 .der이어야 합니다. |
private | 개인 키를 포함합니다. 파일의 형식은 백엔드에 따라 다를 수 있습니다. PEM 인코딩된 파일의 확장자는 .pem이어야 합니다. PKCS#12로 인코딩된 파일의 확장자는 .pfx여야 합니다. 루트 파일 이름은 certs 디렉터리에 있는 해당 공개 키 파일과 동일해야 합니다. 이 폴더는 own 폴더 안에만 존재합니다. |
crl | certs 디렉터리에 있는 모든 CA 인증서에 대한 DER 인코딩된 CRL을 포함합니다. 파일의 파일 확장자는 .crl이어야 합니다. |
첫 번째 연결
처음 연결할 때 클라이언트는 서버 인증서를 신뢰해야 합니다.
클라이언트는 인증서 경고(인증서 상세 정보 포함)를 표시하고 인증서를 신뢰 목록에 저장할 수 있는 가능성을 제공해야 합니다. 예는 Qt OPC UA 뷰어 예시를 참조하세요.
클라이언트가 서버 인증서를 수락하면 다시 연결을 시도할 수 있습니다. 이제 서버가 클라이언트의 인증서를 거부할 수 있습니다. 이는 일반적인 오류 코드 BadSecurityChecksFailed
로 표시됩니다. 서버는 일반적으로 거부된 인증서를 특별한 rejected
폴더에 저장합니다. 관리자는 이 폴더를 신뢰 목록으로 이동하여 클라이언트를 신뢰할 수 있습니다. 이렇게 하면 클라이언트 인증서를 서버 컴퓨터로 수동으로 복사하지 않아도 됩니다.
서버가 클라이언트를 신뢰하면 곧바로 보안 연결을 할 수 있어야 합니다.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.