Skip to content

CMSAuthenticatedDataParser.getUnauthAttrs() always returns null, and AuthenticatedDataParser.getUnauthAttrs() throws ClassCastException #2532

Closed

Description

@xipki

Summary

CMSAuthenticatedDataParser.getUnauthAttrs() never returns the unauthenticated attributes of an AuthenticatedData. It always returns null. Behind that is a second defect: once the first is fixed, the call throws ClassCastException instead.

Both are present in 1.86 and on main (d401e82d3f). The non-streaming CMSAuthenticatedData.getUnauthAttrs() reads the same message correctly.

Defect 1: unauthAttrNotRead is never set

CMSAuthenticatedDataParser.getUnauthAttrs() only reads the attributes if (unauthAttrs == null && unauthAttrNotRead). The constructor sets authAttrNotRead = true but never sets unauthAttrNotRead, so the guard is always false and the method returns null.

CMSAuthEnvelopedDataParser sets both flags in its constructor:

authAttrNotRead = true;
unauthAttrNotRead = true;

Defect 2: ClassCastException in AuthenticatedDataParser.getUnauthAttrs()

ASN1TaggedObject o = (ASN1TaggedObject)nextObject;

nextObject comes from an ASN1SequenceParser, so it is a DLTaggedObjectParser or BERTaggedObjectParser, never an ASN1TaggedObject. The sibling AuthEnvelopedDataParser.getUnauthAttrs() casts to ASN1TaggedObjectParser, which is correct.

Defect 1 hides defect 2 in the cms API. Calling org.bouncycastle.asn1.cms.AuthenticatedDataParser directly hits defect 2 on any message that has unauthAttrs.

Reproducer

The message is generated by BC itself:

Security.addProvider(new BouncyCastleProvider());

CMSAuthenticatedDataGenerator gen = new CMSAuthenticatedDataGenerator();
gen.addRecipientInfoGenerator(new JcePasswordRecipientInfoGenerator(
    CMSAlgorithm.AES128_CBC, "password".toCharArray()).setProvider("BC")
    .setPasswordConversionScheme(0).setSaltAndIterationCount(new byte[20], 1000));
Attribute attr = new Attribute(new ASN1ObjectIdentifier("1.2.3.4"),
    new DERSet(new DERUTF8String("x")));
gen.setUnauthenticatedAttributeGenerator(
    new SimpleAttributeTableGenerator(new AttributeTable(attr)));
byte[] encoded = gen.generate(new CMSProcessableByteArray("hello".getBytes("UTF-8")),
    new JceCMSMacCalculatorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider("BC").build())
    .getEncoded();

// non-streaming: 1 attribute
System.out.println(new CMSAuthenticatedData(encoded).getUnauthAttrs().size());

// streaming, read in the documented order: content, mac, attributes
CMSAuthenticatedDataParser parser = new CMSAuthenticatedDataParser(encoded);
RecipientInformation recipient =
    (RecipientInformation)parser.getRecipientInfos().getRecipients().iterator().next();
Streams.drain(recipient.getContentStream(
    new JcePasswordAuthenticatedRecipient("password".toCharArray()).setProvider("BC")
        .setPasswordConversionScheme(0)).getContentStream());
System.out.println(Arrays.areEqual(parser.getMac(), recipient.getMac()));   // true
System.out.println(parser.getUnauthAttrs());                                // null

If unauthAttrNotRead is set to true (via reflection, to simulate fixing defect 1), the last call throws:

java.lang.ClassCastException: class org.bouncycastle.asn1.DLTaggedObjectParser cannot be cast to class org.bouncycastle.asn1.ASN1TaggedObject
    at org.bouncycastle.asn1.cms.AuthenticatedDataParser.getUnauthAttrs
    at org.bouncycastle.cms.CMSAuthenticatedDataParser.getUnauthAttrs

Suggested fix

  1. In the CMSAuthenticatedDataParser constructor, set this.unauthAttrNotRead = true; next to authAttrNotRead.
  2. In AuthenticatedDataParser.getUnauthAttrs(), cast to ASN1TaggedObjectParser, as AuthEnvelopedDataParser does.
  3. Add a test that reads unauthenticated attributes through CMSAuthenticatedDataParser. No existing test does: NewAuthenticatedDataStreamTest never calls getUnauthAttrs(), which is how both defects went unnoticed.

Activity

  1. dghgit commented on Oct 31, 2026

    @dghgit
    Contributor

    Thanks for the report. Now fixed and up on https://www.bouncycastle.org/betas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions