The following illustrates how to DER encode a value of a SET. The peculiarity of the SET data type is that its components are not ordered (can occur in any order). Which means that the same value can be encoded in a variety of ways. Having: Name ::= SET { first [3] FirstName, middle [1] MiddleName, last [2] LastName } one can encode the value name Name ::= {first "John", middle "J", last "Smith"} as "John" followed by "J" followed by "Smith" or "Smith" followed by "J" followed by "John" and so on. Canonical encoding rules, like DER, require one-to-one mapping between the values and encodings. For this reason, DER restricts the above variations to only one alternative: encode the components of SET in the canonical order. The canonical order is the order in which each component has the tag that is greater than the tag of the previous component. For example, for the Name type that is defined above the canonical order of components is 'middle', 'last', 'first'. In most cases the canonical order is static for a given type (that is, the same for all values of this type). But there is an exception: if the SET type happens to contain a nested untagged choice, the canonical order can vary from one value to another. Namely, DER specifies that one should use the tag of the current selection of this untagged CHOICE in order to position the corresponding component in the ordered list. Let's explain the above on a real example. Here are definitions that are extracted from MTSAbstractService (X.400 series): -- This is an extract from the MTSAbstractService -- {joint-iso-itu-t mhs(6) mts(3) modules(0) mts-abstract-service(1) -- version-1999(1)} MTSAbstractService-Fragment DEFINITIONS IMPLICIT TAGS ::= BEGIN RefusedOperation ::= SET { refused-argument CHOICE { built-in-argument [1] RefusedArgument, refused-extension EXTENSION.&id }, refusal-reason [2] RefusalReason } RefusedArgument ::= INTEGER { user-name (0), user-address (1), encoded-information-types-constraints (2), deliverable-content-types (3), deliverable-maximum-content-length (4), deliverable-security-labels (5), recipient-assigned-redirections (6), restricted-delivery (7), retrieve-registrations (8), -- value 9 reserved for possible future extension to Register arguments restrict (10), permissible-operations (11), permissible-lowest-priority (12), permissible-encoded-information-types (13), permissible-content-types (14), permissible-maximum-content-length (15), permissible-security-context (16) } (0..ub-integer-options) RefusalReason ::= INTEGER { facility-unavailable (0), facility-not-subscribed (1), parameter-unacceptable (2) } (0..ub-integer-options) EXTENSION ::= CLASS { &id ExtensionType, &Type OPTIONAL, &absent &Type OPTIONAL, &recommended Criticality DEFAULT { } } WITH SYNTAX { [&Type [IF ABSENT &absent],] [RECOMMENDED CRITICALITY &recommended,] IDENTIFIED BY &id } ExtensionType ::= CHOICE { standard-extension [0] INTEGER (0..ub-extension-types), private-extension [3] OBJECT IDENTIFIER } Criticality ::= BIT STRING { for-submission (0), for-transfer (1), for-delivery (2) } (SIZE (0..ub-bit-options)) -- critical 'one', non-critical 'zero' -- Upper bounds ub-bit-options INTEGER ::= 16 ub-integer-options INTEGER ::= 256 ub-extension-types INTEGER ::= 256 -- Sample value refusedOperation1 RefusedOperation ::= { refused-argument refused-extension : built-in-argument : restrict, refusal-reason parameter-unacceptable } refusedOperation2 RefusedOperation ::= { refused-argument refused-extension : private-extension : {1 2 3 4 5}, refusal-reason parameter-unacceptable } END Note that the 'refused-argument' component of the RefusedOperation is an untagged CHOICE. How does one construct DER compliant encodings of refusedOperation1 and refusedOperation2? First, let's look at the first value - the 'refuseOperation1': RefusedOperation ::= SET { refused-argument CHOICE { built-in-argument [1] RefusedArgument, refused-extension EXTENSION.&id }, refusal-reason [2] RefusalReason } refusedOperation1 RefusedOperation ::= { refused-argument refused-extension : built-in-argument : restrict, refusal-reason parameter-unacceptable } The 'refusal-reason' component has the tag [2]. In order to decide whether the 'refused-argument' should be put before or after the 'refusal-reason', one should compute its tag. Since the 'refused-argument' is defined as the untagged CHOICE and the CHOICE has the 'built-in-argument' alternative selected, the tag to use for canonical ordering is [1] and a DER compliant encoder should encode the components in the order: 'refused-argument', 'refusal-reason': RefusedOperation SET: tag = [UNIVERSAL 17] constructed; length = 6 refused-argument CHOICE built-in-argument RefusedArgument INTEGER: tag = [1] primitive; length = 1 10 refusal-reason RefusalReason INTEGER: tag = [2] primitive; length = 1 2 PDU successfully encoded, in 8 bytes: 31 06 - tag and length of the RefusedOperation (SET) 81 01 0A - encoded 'refused-argument' 82 01 02 - encoded 'refusal-reason' Now let's see what will happen for the second value of the same type - for the refusedOperation2 value: RefusedOperation ::= SET { refused-argument CHOICE { built-in-argument [1] RefusedArgument, refused-extension EXTENSION.&id }, refusal-reason [2] RefusalReason } refusedOperation2 RefusedOperation ::= { refused-argument refused-extension : private-extension : {1 2 3 4 5}, refusal-reason parameter-unacceptable } This time the 'refused-argument' component has another alternative selected - the 'refused-extension', that has the EXTENSION.&id type. Looking at the definition of the EXTENSION information object class one will find that the notation EXTENSION.&id specifies the type: ExtensionType ::= CHOICE { standard-extension [0] INTEGER (0..ub-extension-types), private-extension [3] OBJECT IDENTIFIER } That is, another untagged CHOICE. So finally the tag, that identifies the position of the 'refused-argument' in the canonically ordered list of components, is the tag of the 'private-extension' alternative or [3]. Which means that for the 'refusedOperation2' the components have to be encoded in reverse order: the encoding of the 'refused-argument' must follow the encoding of the 'refusal-reason': RefusedOperation SET: tag = [UNIVERSAL 17] constructed; length = 9 refusal-reason RefusalReason INTEGER: tag = [2] primitive; length = 1 2 refused-argument CHOICE refused-extension ExtensionType CHOICE private-extension OBJECT IDENTIFIER: tag = [3] primitive; length = 4 { 1 2 3 4 5 } PDU successfully encoded, in 11 bytes: 31 09 - tag and length of the RefusedOperation (SET) 82 01 02 - encoded 'refusal-reason' 83 04 2A030405 - encoded 'refused-argument' You can easily check whether your tool implements DER correctly by compiling the mts.asn attached and attempting to encode the sample values. To compile and run the sample, follow the instructions from the readme-sample.txt file included in the sample zip file.