Errata exists for this version of the document.
OPC UA Secure Conversation (UASC) allows secure communication using binary encoded Messages.
UASC is designed to operate with different TransportProtocols that may have limited buffer sizes. For this reason, OPC UA Secure Conversation will break OPC UA Messages into several pieces (called ‘MessageChunks’) that are smaller than the buffer size allowed by the TransportProtocol. UASC requires a TransportProtocol buffer size that is at least 8 192 bytes.
All security is applied to individual MessageChunks and not the entire OPC UA Message. A Stack that implements UASC is responsible for verifying the security on each MessageChunk received and reconstructing the original OPC UA Message.
All MessageChunks will have a 4-byte sequence assigned to them. These sequence numbers are used to detect and prevent replay attacks.
UASC requires a TransportProtocol that will preserve the order of MessageChunks, however, a UASC implementation does not necessarily process the Messages in the order that they were received.
Figure 11 shows the structure of a MessageChunk and how security is applied to the Message.
Figure 11 – OPC UA Secure Conversation MessageChunk
Every MessageChunk has a Message header with the fields defined in Table 41.
Table 41 – OPC UA Secure Conversation Message header
Name |
Data Type |
Description |
MessageType |
Byte [3] |
A three byte ASCII code that identifies the Message type. The following values are defined at this time: MSGA Message secured with the keys associated with a channel. OPN OpenSecureChannel Message. CLO CloseSecureChannel Message. |
IsFinal |
Byte |
A one byte ASCII code that indicates whether the MessageChunk is the final chunk in a Message. The following values are defined at this time: C An intermediate chunk. F The final chunk. A The final chunk (used when an error occurred and the Message is aborted). This field is only meaningful for MessageType of ‘MSG’ This field is always ‘F’ for other MessageTypes. |
MessageSize |
UInt32 |
The length of the MessageChunk, in bytes. The length starts from the beginning of the MessageType field. |
SecureChannelId |
UInt32 |
A unique identifier for the SecureChannel assigned by the Server. If a Server receives a SecureChannelId which it does not recognize it shall return an appropriate transport layer error. When a Server starts the first SecureChannelId used should be a value that is likely to be unique after each restart. This ensures that a Server restart does not cause previously connected Clients to accidently ‘reuse’ SecureChannels that did not belong to them. |
The Message header is followed by a security header which specifies what cryptography operations have been applied to the Message. There are two versions of the security header which depend on the type of security applied to the Message. The security header used for asymmetric algorithms is defined in Table 42. Asymmetric algorithms are used to secure the OpenSecureChannel Messages. PKCS #1 defines a set of asymmetric algorithms that may be used by UASC implementations. The AsymmetricKeyWrapAlgorithm element of the SecurityPolicy structure defined in Table 35 is not used by UASC implementations.
Table 42 – Asymmetric algorithm Security header
Name |
Data Type |
Description |
SecurityPolicyUriLength |
Int32 |
The length of the SecurityPolicyUri in bytes. This value shall not exceed 255 bytes. If a URI is not specified this value may be 0 or -1. Other negative values are invalid. |
SecurityPolicyUri |
Byte [*] |
The URI of the Security Policy used to secure the Message. This field is encoded as a UTF-8 string without a null terminator. |
SenderCertificateLength |
Int32 |
The length of the SenderCertificate in bytes. This value shall not exceed MaxSenderCertificateSize bytes. If a certificate is not specified this value may be 0 or -1. Other negative values are invalid. |
SenderCertificate |
Byte [*] |
The X.509 v3 Certificate assigned to the sending application Instance. This is a DER encoded blob. The structure of an X.509 v3 Certificate is defined in X.509 v3. The DER format for a Certificate is defined in X690 This indicates what Private Key was used to sign the MessageChunk. The Stack shall close the channel and report an error to the application if the SenderCertificate is too large for the buffer size supported by the transport layer. This field shall be null if the Message is not signed. If the Certificate is signed by a CA, the DER encoded CA Certificate may be appended after the Certificate in the byte array. If the CA Certificate is also signed by another CA this process is repeated until the entire Certificate chain is in the buffer or if MaxSenderCertificateSize limit is reached (the process stops after the last whole Certificate that can be added without exceeding the MaxSenderCertificateSize limit). Receivers can extract the Certificates from the byte array by using the Certificate size contained in DER header (see X.509 v3). Receivers that do not handle Certificate chains shall ignore the extra bytes. |
ReceiverCertificateThumbprintLength |
Int32 |
The length of the ReceiverCertificateThumbprint in bytes. If encrypted the length of this field is 20 bytes. If not encrypted the value may be 0 or -1. Other negative values are invalid. |
ReceiverCertificateThumbprint |
Byte [*] |
The thumbprint of the X.509 v3 Certificate assigned to the receiving application Instance. The thumbprint is the CertificateDigest of the DER encoded form of the Certificate. This indicates what public key was used to encrypt the MessageChunk. This field shall be null if the Message is not encrypted. |
The receiver shall close the communication channel if any of the fields in the security header have invalid lengths.
The SenderCertificate, including any chains, shall be small enough to fit into a single MessageChunk and leave room for at least one byte of body information. The maximum size for the SenderCertificate can be calculated with this formula:
MaxSenderCertificateSize =
MessageChunkSize –
12 - // Header size
4 - // SecurityPolicyUriLength
SecurityPolicyUri -// UTF-8 encoded string
4 - // SenderCertificateLength
4 - // ReceiverCertificateThumbprintLength
20 - // ReceiverCertificateThumbprint
8 - // SequenceHeader size
1 - // Minimum body size
1 - // PaddingSize if present
Padding - // Padding if present
ExtraPadding - // ExtraPadding if present
AsymmetricSignatureSize// If present
The MessageChunkSize depends on the transport protocol but shall be at least 8 192 bytes. The AsymmetricSignatureSize depends on the number of bits in the public key for the SenderCertificate. The Int32FieldLength is the length of an encoded Int32 value and it is always 4 bytes.
The security header used for symmetric algorithms defined in Table 43. Symmetric algorithms are used to secure all Messages other than the OpenSecureChannel Messages. FIPS 197 define symmetric encryption algorithms that UASC implementations may use. FIPS 180-2 and HMAC define some symmetric signature algorithms.
Table 43 – Symmetric algorithm Security header
Name |
Data Type |
Description |
TokenId |
UInt32 |
A unique identifier for the SecureChannel SecurityToken used to secure the Message. This identifier is returned by the Server in an OpenSecureChannel response Message. If a Server receives a TokenId which it does not recognize it shall return an appropriate transport layer error. |
The security header is always followed by the sequence header which is defined in Table 44. The sequence header ensures that the first encrypted block of every Message sent over a channel will start with different data.
Name |
Data Type |
Description |
SequenceNumber |
UInt32 |
A monotonically increasing sequence number assigned by the sender to each MessageChunk sent over the SecureChannel. |
RequestId |
UInt32 |
An identifier assigned by the Client to OPC UA request Message. All MessageChunks for the request and the associated response use the same identifier. |
A SequenceNumber may not be reused for any TokenId. The SecurityToken lifetime should be short enough to ensure that this never happens; however, if it does the receiver should treat it as a transport error and force a reconnect.
The SequenceNumber shall also monotonically increase for all Messages and shall not wrap around until it is greater than 4 294 966 271 (UInt32.MaxValue – 1 024). The first number after the wrap around shall be less than 1 024. Note that this requirement means that a SequenceNumber does not reset when a new TokenId is issued. The SequenceNumber shall be incremented by exactly one for each MessageChunk sent unless the communication channel was interrupted and re-established. Gaps are permitted between the SequenceNumber for the last MessageChunk received before the interruption and the SequenceNumber for first MessageChunk received after communication was re-established. Note that the first MessageChunk after a network interruption is always an OpenSecureChannel request or response. If gaps occur in any other case the receiver shall close the SecureChannel.
The sequence header is followed by the Message body which is encoded with the OPC UA Binary encoding as described in 5.2.9. The body may be split across multiple MessageChunks.
Each MessageChunk also has a footer with the fields defined in Table 45.
Table 45 – OPC UA Secure Conversation Message footer
Name |
Data Type |
Description |
PaddingSize |
Byte |
The number of padding bytes (not including the byte for the PaddingSize). |
|
|
|
Padding |
Byte [*] |
Padding added to the end of the Message to ensure length of the data to encrypt is an integer multiple of the encryption block size. The value of each byte of the padding is equal to PaddingSize. |
ExtraPaddingSize |
Byte |
The most significant byte of a two-byte integer used to specify the padding size when the key used to encrypt the message chunk is larger than 2 048 bits. This field is omitted if the key length is less than or equal to 2 048 bits. |
Signature |
Byte [*] |
The signature for the MessageChunk. The signature includes the all headers, all Message data, the PaddingSize and the Padding. |
The formula to calculate the amount of padding depends on the amount of data that needs to be sent (called BytesToWrite). The sender shall first calculate the maximum amount of space available in the MessageChunk (called MaxBodySize) using the following formula:
MaxBodySize = PlainTextBlockSize * Floor ((MessageChunkSize –
HeaderSize - 1)/CipherTextBlockSize) –
SequenceHeaderSize – SignatureSize;
The HeaderSize includes the MessageHeader and the SecurityHeader. The SequenceHeaderSize is always 8 bytes.
During encryption a block with a size equal to PlainTextBlockSize is processed to produce a block with size equal to CipherTextBlockSize. These values depend on the encryption algorithm and may be the same.
The OPC UA Message can fit into a single chunk if BytesToWrite is less than or equal to the MaxBodySize. In this case the PaddingSize is calculated with this formula:
PaddingSize = PlainTextBlockSize –
((BytesToWrite + SignatureSize + 1) % PlainTextBlockSize);
If the BytesToWrite is greater than MaxBodySize the sender shall write MaxBodySize bytes with a PaddingSize of 0. The remaining BytesToWrite – MaxBodySize bytes shall be sent in subsequent MessageChunks.
The PaddingSize and Padding fields are not present if the MessageChunk is not encrypted.
The Signature field is not present if the MessageChunk is not signed.
MessageChunks are sent as they are encoded. MessageChunks belonging to the same Message shall be sent sequentially. If an error occurs creating a MessageChunk then the sender shall send a final MessageChunk to the receiver that tells the receiver that an error occurred and that it should discard the previous chunks. The sender indicates that the MessageChunk contains an error by setting the IsFinal flag to ‘A’ (for Abort). Table 46 specifies the contents of the Message abort MessageChunk.
Table 46 – OPC UA Secure Conversation Message abort body
Name |
Data Type |
Description |
Error |
UInt32 |
The numeric code for the error. This shall be one of the values listed in Table 55. |
Reason |
String |
A more verbose description of the error. This string shall not be more than 4 096 bytes. A Client shall ignore strings that are longer than this. |
The receiver shall check the security on the abort MessageChunk before processing it. If everything is ok, then the receiver shall ignore the Message but shall not close the SecureChannel. The Client shall report the error back to the application as StatusCode for the request. If the Client is the sender, then it shall report the error without waiting for a response from the Server.
Most Messages require a SecureChannel to be established. A Client does this by sending an OpenSecureChannel request to the Server. The Server shall validate the Message and the ClientCertificate and return an OpenSecureChannel response. Some of the parameters defined for the OpenSecureChannel service are specified in the security header (see 6.7.2) instead of the body of the Message. Table 47 lists the parameters that appear in the body of the Message.
Note that OPC 10000-4 is an abstract specification which defines interfaces that can work with any protocol. This specification provides a concrete implementation for specific protocols. This document is the normative reference for all protocols and takes precedence if there are differences with OPC 10000-4.
Table 47 – OPC UA Secure Conversation OpenSecureChannel Service
Name |
Data Type |
Request |
|
RequestHeader |
RequestHeader |
ClientProtocolVersion |
UInt32 |
RequestType |
SecurityTokenRequestType |
SecurityMode |
MessageSecurityMode |
ClientNonce |
ByteString |
RequestedLifetime |
UInt32 |
|
|
Response |
|
ResponseHeader |
ResponseHeader |
ServerProtocolVersion |
UInt32 |
SecurityToken |
ChannelSecurityToken |
SecureChannelId |
UInt32 |
TokenId |
UInt32 |
CreatedAt |
UtcTime |
RevisedLifetime |
UInt32 |
ServerNonce |
ByteString |
The ClientProtocolVersion and ServerProtocolVersion parameters are not defined in OPC 10000-4 and are added to the Message to allow backward compatibility if OPC UA-SecureConversation needs to be updated in the future. Receivers always accept numbers greater than the latest version that they support. The receiver with the higher version number is expected to ensure backward compatibility.
If OPC UA-SecureConversation is used with the OPC UA-TCP protocol (see 7.1) then the version numbers specified in the OpenSecureChannel Messages shall be the same as the version numbers specified in the OPC UA-TCP protocol Hello/Acknowledge Messages. The receiver shall close the channel and report a Bad_ProtocolVersionUnsupported error if there is a mismatch.
The Server shall return an error response as described in OPC 10000-4 if there are any errors with the parameters specified by the Client.
The RevisedLifetime tells the Client when it shall renew the SecurityToken by sending another OpenSecureChannel request. The Client shall continue to accept the old SecurityToken until it receives the OpenSecureChannel response. The Server has to accept requests secured with the old SecurityToken until that SecurityToken expires or until it receives a Message from the Client secured with the new SecurityToken. The Server shall reject renew requests if the SenderCertificate is not the same as the one used to create the SecureChannel or if there is a problem decrypting or verifying the signature. The Client shall abandon the SecureChannel if the Certificate used to sign the response is not the same as the Certificate used to encrypt the request. Note that datatype is a UInt32 value representing the number of milliseconds instead of the Double (Duration) defined in OPC 10000-4. This optimization is possible because sub-millisecond timeouts are not supported.
The OpenSecureChannel Messages are signed and encrypted if the SecurityMode is not None (even if the SecurityMode is Sign).
The Nonces shall be cryptographic random numbers with a length specified by the SecureChannelNonceLength of the SecurityPolicy.
See OPC 10000-2 for more information on the requirements for random number generators. The OpenSecureChannel Messages are not signed or encrypted if the SecurityMode is None. The Nonces are ignored and should be set to null. The SecureChannelId and the TokenId are still assigned but no security is applied to Messages exchanged via the channel. The SecurityToken shall still be renewed before the RevisedLifetime expires. Receivers shall still ignore invalid or expired TokenIds.
If the communication channel breaks the Server shall maintain the SecureChannel long enough to allow the Client to reconnect. The RevisedLifetime parameter also tells the Client how long the Server will wait. If the Client cannot reconnect within that period it shall assume the SecureChannel has been closed.
The AuthenticationToken in the RequestHeader shall be set to null.
If an error occurs after the Server has verified Message security it shall return a ServiceFault instead of a OpenSecureChannel response. The ServiceFault Message is described in OPC 10000-4.
If the SecurityMode is not None then the Server shall verify that a SenderCertificate and a ReceiverCertificateThumbprint were specified in the SecurityHeader.
Once the SecureChannel is established the Messages are signed and encrypted with keys derived from the Nonces exchanged in the OpenSecureChannel call. These keys are derived by passing the Nonces to a pseudo-random function which produces a sequence of bytes from a set of inputs. A pseudo-random function is represented by the following function declaration:
Byte[] PRF(
Byte[] secret,
Byte[] seed,
Int32 length,
Int32 offset)
Where length is the number of bytes to return and offset is a number of bytes from the beginning of the sequence.
The lengths of the keys that need to be generated depend on the SecurityPolicy used for the channel. The following information is specified by the SecurityPolicy:
- SigningKeyLength (from the DerivedSignatureKeyLength);
- EncryptingKeyLength (implied by the SymmetricEncryptionAlgorithm);
- EncryptingBlockSize (implied by the SymmetricEncryptionAlgorithm).
The pseudo random function requires a secret and a seed. These values are derived from the Nonces exchanged in the OpenSecureChannel request and response. Table 48 specifies how to derive the secrets and seeds when using RSA based SecurityPolicies.
Table 48 – PRF inputs for RSA based SecurityPolicies
Name |
Derivation |
ClientSecret |
The value of the ClientNonce provided in the OpenSecureChannel request. |
ClientSeed |
The value of the ClientNonce provided in the OpenSecureChannel request. |
ServerSecret |
The value of the ServerNonce provided in the OpenSecureChannel response. |
ServerSeed |
The value of the ServerNonce provided in the OpenSecureChannel response. |
The parameters passed to the pseudo random function are specified in Table 49.
Table 49 – Cryptography key generation parameters
Key |
Secret |
Seed |
Length |
Offset |
ClientSigningKey |
ServerSecret |
ClientSeed |
SigningKeyLength |
0 |
ClientEncryptingKey |
ServerSecret |
ClientSeed |
EncryptingKeyLength |
SigningKeyLength |
ClientInitializationVector |
ServerSecret |
ClientSeed |
EncryptingBlockSize |
SigningKeyLength+ EncryptingKeyLength |
ServerSigningKey |
ClientSecret |
ServerSeed |
SigningKeyLength |
0 |
ServerEncryptingKey |
ClientSecret |
ServerSeed |
EncryptingKeyLength |
SigningKeyLength |
ServerInitializationVector |
ClientSecret |
ServerSeed |
EncryptingBlockSize |
SigningKeyLength+ EncryptingKeyLength |
The Client keys are used to secure Messages sent by the Client. The Server keys are used to secure Messages sent by the Server.
The SSL/TLS specification defines a pseudo random function called P_HASH which is used for this purpose. The function is iterated until it produces enough data for all of the required keys. The Offset in Table 49 references to the offset from the start of the generated data.
The P_ hash algorithm is defined as follows:
P_HASH(secret, seed) = HMAC_HASH(secret, A(1) + seed) +
HMAC_HASH(secret, A(2) + seed) +
HMAC_HASH(secret, A(3) + seed) + ...
Where A(n) is defined as:
A(0) = seed
A(n) = HMAC_HASH(secret, A(n-1))
+ indicates that the results are appended to previous results.
Where ‘HASH’ is a hash function such as SHA256. The hash function to use depends on the SecurityPolicyUri.
The contents of the MessageChunk shall not be interpreted until the Message is decrypted and the signature and sequence number verified.
If an error occurs during Message verification the receiver shall close the communication channel. If the receiver is the Server, it shall also send a transport error Message before closing the channel. Once the channel is closed the Client shall attempt to re-open the channel and request a new SecurityToken by sending an OpenSecureChannel request. The mechanism for sending transport errors to the Client depends on the communication channel.
The receiver shall first check the SecureChannelId. This value may be 0 if the Message is an OpenSecureChannel request. For other Messages, it shall report a Bad_SecureChannelUnknown error if the SecureChannelId is not recognized. If the Message is an OpenSecureChannel request and the SecureChannelId is not 0 then the SenderCertificate shall be the same as the SenderCertificate used to create the channel.
If the Message is secured with asymmetric algorithms, then the receiver shall verify that it supports the requested Securi tyPolicy. If the Message is the response sent to the Client, then the SecurityPolicy shall be the same as the one specified in the request. In the Server, the SecurityPolicy shall be the same as the one used to originally create the SecureChannel. The receiver shall check that the Certificate is trusted first and return Bad_CertificateUntrusted on error. The receiver shall then verify the SenderCertificate using the rules defined in OPC 10000-4. The receiver shall report the appropriate error if Certificate validation fails. The receiver shall verify the ReceiverCertificateThumbprint and report a Bad_CertificateUnknown error if it does not recognize it.
If the Message is secured with symmetric algorithms, then a Bad_SecureChannel TokenUnknown e rror shall be reported if the TokenId refers to a SecurityToken that has expired or is not recognized.
If decryption or signature validation fails, then a Bad_SecurityChecksFailed error is reported. If an implementation allows multiple SecurityModes to be used the receiver shall also verify that the Message was secured properly as required by the SecurityMode specified in the OpenSecureChannel request.
After the security validation is complete the receiver shall verify the RequestId and the SequenceNumber. If these checks fail a Bad_SecurityChecksFailed error is reported. The RequestId only needs to be verified by the Client since only the Client knows if it is valid or not.
At this point the SecureChannel knows it is dealing with an authenticated Message that was not tampered with or resent. This means the SecureChannel can return secured error responses if any further problems are encountered.
Stacks that implement UASC shall have a mechanism to log errors when invalid Messages are discarded. This mechanism is intended for developers, systems integrators and administrators to debug network system configuration issues and to detect attacks on the network.