elastic/elasticsearch · error · IOException

Invalid DER: can't parse primitive entity

Error message

Invalid DER: can't parse primitive entity

What it means

Thrown by Asn1Object.getParser() when getParser() is called on a primitive (non-constructed) ASN.1 entity. Only constructed entities (tag bit 0x20 set, e.g. SEQUENCE, SET) have nested content that can be re-parsed; primitive entities hold a leaf value.

Source

Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DerParser.java:217

            return length;
        }

        public byte[] getValue() {
            return value;
        }

        public boolean isConstructed() {
            return (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED;
        }

        /**
         * For constructed field, return a parser for its content.
         *
         * @return A parser for the construct.
         */
        public DerParser getParser() throws IOException {
            if (isConstructed() == false) {
                throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$
            }

            return new DerParser(value);
        }

        /**
         * Get the value as integer
         *
         * @return BigInteger
         */
        public BigInteger getInteger() throws IOException {
            if (type != Type.INTEGER) throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$

            return new BigInteger(value);
        }

        public String getString() throws IOException {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Regenerate the key with a standard tool: `openssl ecparam -genkey -name prime256v1 -out ec.pem` or `openssl pkcs8 -topk8`.
  2. If you are parsing custom DER, re-check the expected schema and ensure you only call getParser() on constructed elements.
  3. Use `isConstructed()` as a guard before calling getParser() to produce a clearer error.
  4. Inspect with `openssl asn1parse -inform DER -in key.der` to verify element types.

Example fix

// before: unconditional sub-parse
DerParser.Asn1Object choice = parser.readAsn1Object();
DerParser inner = choice.getParser(); // throws if primitive

// after: guard the call
if (choice.isConstructed() == false) {
    throw new IOException("expected constructed element but found primitive tag 0x" + Integer.toHexString(choice.getTag()));
}
DerParser inner = choice.getParser();
Defensive patterns

Strategy: type-guard

Type guard

public static boolean isConstructed(DerParser.Asn1Object o) {
    return o != null && o.isConstructed();
}

// Usage:
// DerParser.Asn1Object elem = parser.readAsn1Object();
// if (!isConstructed(elem)) throw new IOException("expected constructed element");
// DerParser inner = elem.getParser();

Prevention

When it happens

Trigger: Code calls asn1Object.getParser() on an object whose CONSTRUCTED bit (0x20) is not set. Common in parseEcDer (line 607) where the code calls choice.getParser() on the third element — if the input is misencoded and that element is primitive instead of constructed, this fires.

Common situations: Malformed EC private key where the expected [0] or [1] tagged constructed element is encoded as primitive, or a DER blob whose structure does not match the expected PKCS#1/PKCS#8/SEC1 layout.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/7ccb03e0737c96cc. Report an issue: GitHub.