private static void sanitizeAndTransformAttributeNames(final Map<String, Object> attributes,
                                                           final Service webApplicationService) {
        if (webApplicationService instanceof final WebApplicationService was && was.getFormat() == ValidationResponseType.JSON) {
            LOGGER.trace("Skipping attribute name sanitization for [{}]", webApplicationService);
            return;
        }

        LOGGER.trace("Sanitizing attribute names in preparation of the final validation response");
        val encodedAttributes = attributes.keySet()
            .stream()
            .filter(DefaultCasProtocolAttributeEncoder::getSanitizingAttributeNamePredicate)
            .map(attribute -> {
                val values = attributes.get(attribute);
                LOGGER.trace("Encoding attribute [{}] with value(s) [{}]", attribute, values);
                return Pair.of(ProtocolAttributeEncoder.encodeAttribute(attribute), values);
            })
            .collect(Collectors.toSet());

        if (!encodedAttributes.isEmpty()) {
            LOGGER.info("Found [{}] attribute(s) that need to be sanitized/encoded.", encodedAttributes.size());
            attributes.keySet().removeIf(DefaultCasProtocolAttributeEncoder::getSanitizingAttributeNamePredicate);
            encodedAttributes.forEach(attribute -> {
                val key = attribute.getKey();
                LOGGER.trace("Sanitized attribute name to be [{}]", key);
                attributes.put(key, transformAttributeValueIfNecessary(attribute.getValue()));
            });
        }
    }
The default implementation of the attribute
encoder that will use a per-service key-pair
to encrypt the credential password and PGT
when available. All other attributes remain in
place.

@author Misagh Moayyed
@since 4.1
core/cas-server-core-services-authentication/src/main/java/org/apereo/cas/authentication/support/DefaultCasProtocolAttributeEncoder.java