How to connect to an OPC-UA server which requires certificate based user authentication using node-opcua based client
Asked Answered
C

1

6

I am using the node-opcua library to build an OPC UA client. I have use the basic example to connect to an OPC-UA server (prosys OPC-UA simulation server), but now I would like to make my client support various authentication methods.

I am able to get username + password based authentication working with the following code:

const client = OPCUAClient.create(options);
await client.connect(endpointUrl);
let userIdentity = {type: opcua.UserTokenType.UserName, userName: "bruce", password: "test" };
const session = await client.createSession(userIdentity); 

On the prosys OPC-UA simulation server I have checked the "Username + Password" authentication method and created the matching user.

Now, however I would like to get the X.509 certificate based user authentication working. Does anyone have a working example in node-opcua and directions on generating certificates with OpenSSL?

Coniine answered 9/10, 2020 at 20:50 Comment(0)
C
9

So I figured it out! Here's the answer for anyone else that might want it (or my future self)

First, it seems like the certificate needs to have the right usage and extensions. When I generated a certificate without these, it didn't work. Create a file called user-key.conf with content similar to:

[ 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 = CA:FALSE
nsCertType = client, server
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign
extendedKeyUsage= serverAuth, clientAuth
nsComment = "Bruces User Cert"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
subjectAltName = URI:urn:opcua:user:bruce,IP: 127.0.0.1

[ subject ]
countryName = US
stateOrProvinceName = TX
localityName = Houston
organizationName = AL
commonName = bruce

Note, in this example, I am generating a self-signed certificate. I am not a security expert, so I don't know the implication of all these claims and I haven't tested to see which ones are absolutely necessary. Some information I found seemed to suggest that the commonName should correspond to the usernames in the OPC server, but this is the only detail that I learned in my research.

Next, use openssl with this configuration file to create a certificate and private key pair:

> openssl.exe req -x509 -days 365 -new -out bruce1.pem -keyout bruce1_key.pem -config user-key.conf

For some (probably a good, security related) reason, openssl forces you to specify a passphrase to protect the private key. So far, I have not figured out how to decrypt this within node.js, so I found that I can remove it with the following command:

> openssl.exe rsa -in bruce1_key.pem -out bruce1_key_nopass.pem

Then, at least with the OPC-UA server I was testing with (prosys OPC-UA simulation server), it was necessary to get the certificate in .der format:

> openssl.exe x509 -inform PEM -outform DER -in bruce1.pem -out bruce1.der

This .der file need to be loaded into the OPC-UA server. For prosys this was done by copying it into the .\prosys-opc-ua-simulation-server\USERS_PKI\CA\certs folder.

Within node.js, the following code will connect using the generated certificate files:

const client = OPCUAClient.create(options);
await client.connect(endpointUrl);
let userIdentity = {
    type: UserTokenType.Certificate,
    certificateData: fs.readFileSync('./user-certificates/bruce1.pem'),
    privateKey: fs.readFileSync('./user-certificates/bruce1_key_nopass.pem','utf8')
}

const session = await client.createSession(userIdentity); 

This results in a successful connection to the prosys OPC-UA client. I am sure there are some future improvements that can be made, like doing the passphrase decryption within node.js.

Coniine answered 9/10, 2020 at 21:34 Comment(4)
You shouldn't put "localhost" or "127.0.0.1" into a certificate.Minhminho
I get this error: "The URI specified in the ApplicationDescription does not match the URI in the certificate."(BadCertificateUriInvalid). Can you explain how I know what to write in the subjectAltName field, and how I can know what the value of ApplicationDescription.ApplicationUri is (or do I also set it in the config file?)?Misdoing
@KevinHerron can you please provide options used in const client = OPCUAClient.create(options), Or can you tell do we have pass certificateFile & privateKeyFile in optionsExtrasensory
@Extrasensory In my example, the certificate and key are passed when creating the session. When creating the client, the options are something like let options={applicationName: 'testClient', connectionStrategy: {maxRetry: 1}, securityMode: MesseageSecurityMode.None, securityPolicy: SecurityPolicy.None, endpointMustExist: true}Coniine

© 2022 - 2024 — McMap. All rights reserved.