Custom Certificate Extensions
Customized extensions are added and removed in the EJBCA System Configuration page on the Custom Certificate Extensions tab.
Overview
A simple extension only containing a static value can be added using the already implemented class BasicCertificateExtension and more advanced custom extensions can be made available by implementing the org.cesecore.certificates.certificate.certextensions.CustomCertificateExtension interface and making the implementation available on the classpath using Java's ServiceLoader. For more information, refer to BasicCertificateExtension.java. By implementing the marker interface and setting property fields and default values, EJBCA can auto-generate a properties table for the extension.
Configuring Custom Certificate Extensions
Certificate extensions are configured on the Custom Certificate Extensions tab (CA UI → System Configuration → Custom Certificate Extensions). Only administrators who are granted the access rule '/system_functionality/edit_available_custom_certificate_extensions' are allowed to add and remove custom certificate extensions.
The following properties are available for each extension:
Property | Description |
---|---|
OID | The unique Object Identifier (OID) of the extension. Mandatory value. May also be defined as a Custom Certificate Extensions#Wildcard OID. |
Display Name | Display name of the extension in the Edit Certificate Profile page. Mandatory value. |
Extension Class | Full class name of the CertificateExtension implementation class. Mandatory value. |
Critical | Defines if the extension should be marked as critical in the certificate. |
Required | By default enabled, making the extension required in the request. Clearing Required allows a request to optionally contain the defined extension. In its absence, it will be ignored. |
Properties | Properties are inherent to each extension implementation. |
When adding a new Custom Certificate Extension, the OID and the Display Name are specified and a new Extension with the following default values is created:
- Class Path: org.cesecore.certificates.certificate.certextensions.BasicCertificateExtension
- Critical: False
- Properties: Empty list of properties
Click on the newly created extension to edit these values.
After extensions have been added, it is possible to select them for a certificate profile on the Edit Certificate Profile page.
Using the Required flag
When an extension is selected to be required (default) and the extension properties dynamic=true is set, a value has to be provided. It can either be a default value, specified in the extension properties, or a value specified as Custom Certificate Extension Data (see below). Sometimes there is a need to specify an extension as optional so the caller (an RA typically) can decide if the extension should be included or not. By setting Required=false an extension with a dynamic value can be excluded by not providing it in the request.
Certificate Extension Definition and ASN.1
The definition of a certificate extension is found in RFC5280. Specifically the ASN.1 encoding of extensions in RFC5280 section 4.1.
The section specifies that the extension value (extnValue) is an octet string that encapsulates the actual extension sequence.
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
-- contains the DER encoding of an ASN.1 value
-- corresponding to the extension type identified
-- by extnID
}
This encapsulation is handled by EJBCA, for the value type you enter. The actual ASN.1 for example, if you choose to add a DEROCTETSTRING extension value, as specified below, will be an OCTET STRING encapsulated in an OCTET STRING. Likewise, the hex encoded data that you enter as a DEROBJECT is the "DER encoding of an ASN.1 value corresponding to the extension type" as specified in the ASN.1 block above.
OCSP Must Staple
Example extension configuration for the OCSP Must Staple TLS feature extension:
- Object Identifier: 1.3.6.1.5.5.7.1.24
- Label: OCSP Must Staple
- Extension Class: Basic Certificate Extension
- Critical: false
- Required: false
- Properties:
- Dynamic: false
- Encoding: DEROBJECT
- Value: 3003020105
Or for illustrative purposes, you may also set the following:
- Properties:
- Dynamic: true
- Encoding: DEROBJECT
- Value: (empty)
Checking Custom certificate extension data in the end entity profile will bring up a Certificate Extension Data entry field when adding/editing end entities.
If this field is left empty (and no static value has been specified for the extension), generating a certificate for the end entity will result in no extension being added to the issued certificate.
Entering the following data in the Certificate Extension Data entry field will add the OCSP Must Staple extension to the issued certificate:
1.3.6.1.5.5.7.1.24.value=3003020105
Content in the issued certificate (from dumpasn1 ...):
SEQUENCE {
OBJECT IDENTIFIER '1 3 6 1 5 5 7 1 24'
OCTET STRING, encapsulates {
SEQUENCE {
INTEGER 5
}
}
}
NFType for the 5G System
Example extension configuration for the NFType for the 5G System as specified in RFC9310, and defined by the NFTypes Certificate Extension:
NFTypes ::= SEQUENCE SIZE (1..MAX) OF NFType
NFType ::= IA5String (SIZE (1..32))
As a custom extension, this would be configured like the below:
- Object Identifier: 1.3.6.1.5.5.7.1.34
- Label: nfTypes
- Extension Class: Basic Certificate Extension
- Critical: false
- Required: true | false
- Properties:
- Dynamic: true | false
- Encoding: DEROBJECT
- Value: 302E2E2E74797065312E2E7479706532 (hex encoding of the ASN.1 sequence of 2 IA5Strings type1 and type2: '0...type1..type2')
See above for how dynamic: true is processed.
[ default ]
oid_section = additional_oids
[ additional_oids ]
nfTypes = 1.3.6.1.5.5.7.1.34
[ nftypes_reqext ]
nfTypes = ASN1:SEQUENCE:nftypes_seq
[ nftypes_seq ]
nfTypes.0 = IA5STRING:"type1"
nfTypes.1 = IA5STRING:"type2"
Content in the issued certificate (from dumpasn1 ...):
SEQUENCE {
OBJECT IDENTIFIER '1 3 6 1 5 5 7 1 34'
OCTET STRING, encapsulates {
SEQUENCE {
IA5String 'type1'
IA5String 'type2'
}
}
}
3GPP TS29.510 contains all possible NFType string values. A snapshot of the types in release 17 of this document is available in RFC9310, Appendix A.
Wildcard OID
The extension OID may be configured as a wildcard, for example, *.1.2.3. Doing this will match the configuration with any requested OID, matching the pattern (thus ending with .1.2.3). Once matched, the dynamic value from the request will be used in the certificate. Since the wildcard in itself is not a valid OID, an actual OID which matches the wildcard must always be present in the request (unless Required is cleared, in which case it is ignored). Hence, wildcard configurations cannot fall back on the defined static value if the extension is missing from the request.
Removing a Custom Certificate Extension
If removing a custom certificate extension, currently in use in a certificate profile, from the list of available extensions in the Custom Certificate Extensions, a warning is displayed listing the certificate profiles using the extension. The extension will not be removed from the certificate profile since it is still used but the removed extension will not be usable or be part of any certificate issued after the removal and will be displayed as OID (No longer used. Please unselect this option) in the Edit Certificate Profile page. To remove an extension in use from the certificate profile, manually clear the extension from the list of Used Custom Certificate Extensions on the Edit Certificate Profile page.
Basic Certificate Extension
In order to create a Basic Certificate Extension, you set the Class Path to 'org.cesecore.certificates.certificate.certextensions.BasicCertificateExtension' (default setting) and specify the properties encoding and value (value is optional if there is a property 'dynamic=true').
The following table lists available encodings and how their value is interpreted.
Encoding | Value |
---|---|
DERBITSTRING | A string containing the characters '0' and '1'. The value you enter will be encoded into an ASN.1 BITSTRING with the value entered. For example "10011011". |
DERINTEGER | A string containing digits only in decimal digits. The value you enter will be encoded into an ASN.1 INTEGER with the value entered. For example "123". |
DEROCTETSTRING | A string containing hex data representation. The value you enter will be encoded into an ASN.1 OCTET STRING with the value entered. For example "my string". |
DERBOOLEAN | The string 'true' or 'false'. The value you enter will be encoded into an ASN.1 BOOLEAN. |
DERPRINTABLESTRING | A string containing valid printable string characters (a-z, 0-9). The value you enter will be encoded into an ASN.1 PRINTABLE STRING, with the limitations on character set that comes with. |
DERUTF8STRING | A string in UTF-8 format. The value you enter will be encoded into an ASN.1 UTF8 STRING, with less limitations on character set than printable string. |
DERIA5STRING | An ASN.1 IA5String containing valid printable string characters (a-z, 0-9). |
DERNULL | Empty value. This is encoded into an ASN.1 NULL, which is actually a tag and thus encoded into ASN.1 bytes. |
DEROBJECT | The hex encoded DER value of the extensions. You can use this to create an extension with sequences etc. The hex encoded data that you enter is the "DER encoding of an ASN.1 value corresponding to the extension type" as specified in RFC5280 section 4.1 (see above). |
RAW | The hex encoded byte array value of the extensions. You can supply any data hex encoded to be used as the extension value bytes, also data that violates RFC5280. Do not use unless you have a legacy reason needed to create invalidly encoded certificates. |
Examples of certificate extensions that you can configure with the BasicCertificateExtension are given in 'src/java/certextensions.properties'.
- MS application policies
- NetscapeCertType
- NetscapeComment
- NetscapeCARevocationURL
- NetscapeRevocationURL
- ...
If the property dynamic is configured to true, the extension value may be taken from extension data supplied with the end entity. For more information on how to add extension data using the Admin Web, see Custom Certificate Extension Data. If dynamic is true and no data is supplied with the end entity the extension value is taken from the value field, if not empty.
Creating a Custom Extension from Existing Certificate
If you have an existing certificate with a specific extension that you want to add in EJBCA, and the extension is not already present as an option in the EJBCA UI, you can extract the extension from the existing certificate and add it as a custom extension.
If you have a certificate with Policy Constraints, an OpenSSL dump can look like:
>openssl x509 -in cert.pem -text
...
X509v3 Policy Constraints:
Require Explicit Policy:0, Inhibit Policy Mapping:0
X509v3 Inhibit Any Policy:
0
...
To add this Policy Constraints extension to EJBCA, do the following.
Extract the actual DER encoded data:
TEXT>openssl asn1parse -in cert.pem -i ... 1555:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Policy Constraints 1560:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:3006800100810100 1570:d=4 hl=2 l= 10 cons: SEQUENCE 1572:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Inhibit Any Policy 1577:d=5 hl=2 l= 3 prim: OCTET STRING [HEX DUMP]:020100 ...
- Then, to add the value 3006800100810100 as a DEROBJECT custom certificate extension:
- Go to System Configuration → Custom Certificate Extensions and specify the following:
- OID=2.5.29.36
- Label=PolicyConstraints_Explicit0_Inhibit0 (any label that you find appropriate and logical for your system setup).
- Click Add.
- Click Edit for the newly created entry and specify the following:
- Dynamic=false
- Encoding=DEROBJECT
- Value=3006800100810100
- Click Save.
- Go to System Configuration → Custom Certificate Extensions and specify the following:
Next, edit a certificate profile and select to use PolicyConstraints_Explicit0_Inhibit0
in the list of Used Custom Certificate Extensions. Lastly, issue the certificate and verify that the extension is there and as expected.
Implementing an Advanced Certificate Extension
To create an advanced extension, you need to create a Java class implementing the CustomCertificateExtension interface class. We recommend extending the StandardCertificateExtension class. The method getValue is required and the current user data, CA, and certificate profile are sent to the extension in order to generate dynamic extensions.
In addition to static values defined under Admin Web>System Configuration>Custom Certificate Extensions, it is also possible to get values from the extension data in the end entity's extended information store. A good choice is to use the extension oid as a prefix to the key of the property to associate the value with this extension.
The following shows an example of an advanced extension. To add this extension to EJBCA, deploy the class as part of the ejbca.ear file, and it should appear in the list of Extension Classes when editing a custom extension in System Configuration>Custom Certificate Extensions. The file is found automatically due to implementing the CustomCertificateExtension interface.
public class SomeAdvancedCertificateExtension extends StandardCertificateExtension implements CustomCertificateExtension {
private static String PROPERTY_SOMEPROPERTY = "someproperty";
public SomeAdvancedCertificateExtension() {
super.setDisplayName("SomeAdvancedCertificateExtension");
}
@Override
public Map<String, String[]> getAvailableProperties() {
Map<String, String[]> map = new HashMap<>();
map.put(PROPERTY_SOMEPROPERTY, new String[]{"a", "b", "c"});
return map;
}
/**
* The main method that should return a byte[] with the ASN.1 encoded data to be put
in the extension valud
* using the input data (optional) or defined properties (optional)
*
* @see
org.cesecore.certificates.certificate.certextensions.CertificateExtension#getValueEncoded
*/
@Override
public byte[] getValueEncoded(EndEntityInformation userData, CA ca, CertificateProfile
certProfile, PublicKey userPublicKey,
PublicKey caPublicKey, CertificateValidity val )
throws CertificateExtentionConfigurationException, CertificateExtensionException {
try {
// Optionally get dynamic values for this extension
//String dynamicValue = userData.getExtendedinformation().getExtensionData
(getOID() + ".someotherproperty");
String value = getProperties().getProperty("FOO");
DERPrintableString asn1value = new DERPrintableString(value);
return asn1value.toASN1Primitive().getEncoded();
} catch (IOException e) {
throw new CertificateExtensionException("Error constructing certificate
extension SomeAdvancedCertificateExtension.", e);
}
}
/**
* @deprecated use getValueEncoded instead.
*/
public ASN1Encodable getValue(EndEntityInformation userData, CA ca, CertificateProfile
certProfile, PublicKey userPublicKey, PublicKey caPublicKey, CertificateValidity val)
throws CertificateExtensionException, CertificateExtentionConfigurationException {
throw new UnsupportedOperationException("Use getValueEncoded instead");
}
}