为什么 Jasypt 尝试解密 Camel 属性 占位符,而不考虑 ENC( 前缀?

Why does Jasypt try to decrypt Camel Property Placeholders, regardless of the ENC( prefix?

在 JBoss Fuse 6.1.0-379 中部署的蓝图应用程序中,我想保护用于创建数据库连接的密码。我阅读了 this 文章并将 <enc:property-placeholder> 添加到蓝图配置中。然而,我的蓝图配置有许多 属性 占位符,而且 Jasypt Placeholder Resolver 似乎试图解密我在 Camel 上下文中定义的所有占位符。当 Blueprint Context 启动时,出现以下异常:

11:59:51,233 | ERROR | t-379-dmz/deploy | BlueprintCamelContext            | 151 - org.apache.camel.camel-blueprint - 2.12.0.redhat-610379 | Error occurred during starting Camel: CamelContext(camel-5) due Failed to create route route7: Route(route7)[[From[{{uri}}]] -> [Log[logging]]] because of Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
org.apache.camel.FailedToCreateRouteException: Failed to create route route7: Route(route7)[[From[{{uri}}]] -> [Log[logging]]] because of Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:182)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:778)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:1955)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:1705)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:1579)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:1547)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.blueprint.BlueprintCamelContext.start(BlueprintCamelContext.java:177)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
    at org.apache.camel.blueprint.BlueprintCamelContext.maybeStart(BlueprintCamelContext.java:209)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
    at org.apache.camel.blueprint.BlueprintCamelContext.serviceChanged(BlueprintCamelContext.java:147)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
    at org.apache.felix.framework.util.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:934)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.util.EventDispatcher.fireEventImmediately(EventDispatcher.java:795)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.util.EventDispatcher.fireServiceEvent(EventDispatcher.java:544)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:4666)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.registerService(Felix.java:3674)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:347)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.aries.blueprint.container.BlueprintContainerImpl.registerService(BlueprintContainerImpl.java:448)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
    at org.apache.aries.blueprint.container.BlueprintContainerImpl.doRun(BlueprintContainerImpl.java:383)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
    at org.apache.aries.blueprint.container.BlueprintContainerImpl.run(BlueprintContainerImpl.java:261)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
    at org.apache.aries.blueprint.container.BlueprintExtender.createContainer(BlueprintExtender.java:270)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
    at org.apache.aries.blueprint.container.BlueprintExtender.modifiedBundle(BlueprintExtender.java:233)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
    at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:500)[11:org.apache.aries.util:1.0.1.redhat-610379]
    at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:433)[11:org.apache.aries.util:1.0.1.redhat-610379]
    at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$AbstractTracked.track(BundleHookBundleTracker.java:725)[11:org.apache.aries.util:1.0.1.redhat-610379]
    at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.bundleChanged(BundleHookBundleTracker.java:463)[11:org.apache.aries.util:1.0.1.redhat-610379]
    at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$BundleEventHook.event(BundleHookBundleTracker.java:422)[11:org.apache.aries.util:1.0.1.redhat-610379]
    at org.apache.felix.framework.util.SecureAction.invokeBundleEventHook(SecureAction.java:1103)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.util.EventDispatcher.createWhitelistFromHooks(EventDispatcher.java:696)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.util.EventDispatcher.fireBundleEvent(EventDispatcher.java:484)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.fireBundleEvent(Felix.java:4650)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.run(Felix.java:2123)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.runInContext(Felix.java:2147)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.Felix.startBundle(Felix.java:2121)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:955)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundle(DirectoryWatcher.java:1247)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundles(DirectoryWatcher.java:1219)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startAllBundles(DirectoryWatcher.java:1208)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:503)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:291)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
    at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:480)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.util.CamelContextHelper.getMandatoryEndpoint(CamelContextHelper.java:71)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.model.RouteDefinition.resolveEndpoint(RouteDefinition.java:192)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:106)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:112)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.model.FromDefinition.resolveEndpoint(FromDefinition.java:72)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultRouteContext.getEndpoint(DefaultRouteContext.java:88)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:890)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:177)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    ... 38 more
Caused by: org.apache.camel.RuntimeCamelException: org.jasypt.exceptions.EncryptionOperationNotPossibleException
    at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1363)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.util.ObjectHelper.invokeMethod(ObjectHelper.java:1005)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.blueprint.BlueprintPropertiesParser.parseProperty(BlueprintPropertiesParser.java:137)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
    at org.apache.camel.component.properties.DefaultPropertiesParser.createPlaceholderPart(DefaultPropertiesParser.java:201)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.component.properties.DefaultPropertiesParser.doParseUri(DefaultPropertiesParser.java:105)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.component.properties.DefaultPropertiesParser.parseUri(DefaultPropertiesParser.java:51)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:160)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:119)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.resolvePropertyPlaceholders(DefaultCamelContext.java:1155)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:478)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    ... 46 more
Caused by: org.jasypt.exceptions.EncryptionOperationNotPossibleException
    at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.decrypt(StandardPBEByteEncryptor.java:918)
    at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:725)
    at org.apache.karaf.jaas.jasypt.handler.EncryptablePropertyPlaceholder.getProperty(EncryptablePropertyPlaceholder.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.7.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)[:1.7.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.7.0_25]
    at java.lang.reflect.Method.invoke(Method.java:606)[:1.7.0_25]
    at org.apache.camel.util.ObjectHelper.invokeMethod(ObjectHelper.java:1001)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
    ... 54 more

我创建了一个带有蓝图上下文的测试包,它只包含一个在 Camel 上下文中定义的占位符 属性,没有使用加密的 ENC() 占位符语法。我刚刚添加了 <enc:property-placeholder> 并且捆绑包无法以相同的异常启动 (org.jasypt.exceptions.EncryptionOperationNotPossibleException)。

这是期望的行为吗?

我的蓝图配置:

<?xml version="1.0" encoding="UTF-8"?> 
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
           xmlns:enc="http://karaf.apache.org/xmlns/jasypt/v1.0.0"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0">


    <cm:property-placeholder persistent-id="encrypt.config" update-strategy="reload" >
        <cm:default-properties>
            <cm:property name="uri" value="timer://foo?fixedRate=true&amp;period=6000"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <enc:property-placeholder>
        <enc:encryptor class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
            <property name="config">
                <bean class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
                    <property name="algorithm" value="PBEWithMD5AndDES" />
                    <property name="password" value="password" />
                </bean>
            </property>
        </enc:encryptor>
    </enc:property-placeholder>

     <camelContext xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns="http://camel.apache.org/schema/blueprint"
                  xsi:schemaLocation="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="{{uri}}"/>
            <log message="logging" loggingLevel="INFO" id="logBeforeService"></log>
        </route>
     </camelContext>

</blueprint>

编辑:来自 RedHat 支持的回复

所以这是一个已知问题,并且有几个 Jira 问题 (here and here),而且这个问题似乎已在较新版本的 Camel 中得到解决。我已经使用名为 jboss-fuse-6.1.0.redhat-379-r1p3 的补丁提供的版本 2.12.0.redhat-611412 进行了测试,并且不再抛出异常。

不管我之前说过什么,我对这个实现都很满意。如果无法解密实际加密的值,我希望抛出异常,而这正是发生的情况。我将加密值修改为 ENC(invalid_and_should_throw_exception),并抛出异常,正如我所期望的那样。

Caused by: org.jasypt.exceptions.EncryptionOperationNotPossibleException

编辑:更简洁的答案

Camel-Blueprint 在解析 属性 占位符值的方式方面与 Camel-Core 的行为不同。 Camel-Core 要求开发人员为 camel 属性 语法 [1] 定义一个 Camel 属性 占位符解析器,它解析 Camel 上下文中的属性。显然,这背后的原因是为了避免 spring 属性 语法 [2] 和 Camel 简单表达式语言语法 [3] 之间的冲突。开发人员可以选择通过添加额外的配置来选择性地桥接 Spring 属性 Placeholder Resolver 与 Camel。

[1 - Camel 属性 语法]

{{org.my.prop}}

[2 - Spring 属性 语法]

${org.my.prop}

[3 - 简单表达式语言语法]

${exchange.body}

在 Camel-Blueprint 中,Blueprint 属性 Placeholder Resolvers 和 Camel Context 之间的桥接是自动发生的。创建 Blueprint Camel 上下文时,会将 Blueprint Bundle Context 注入其中。借助 Blueprint Bundle Context,Camel 从中提取所有 bean 并确定它们是否可分配给 Apache Aries 实现 Abstract属性Placeholder。对于您定义的 属性 Placeholder Resolvers 的每个实例,Camel 能够调用它们的 resolve属性 方法,而无需解析每个解析器定义的 属性 语法.

因为 Jasypt 属性 Placeholder Resolver 需要占位符语法 [4],它会忽略所有与此语法匹配的内容。因为 Camel-Blueprint 绕过了确保 属性 语法的验证,我们最终会遇到一个场景,Camel 告诉 Jasypt Placeholder Resolver 解密我们尝试在 Camel 上下文中使用的每个 属性 .这当然会抛出异常,因为您正在尝试解密尚未加密的 属性。

[4 - Jasypt 蓝图 属性 语法]

ENC(encrypted.value)

解决方案:

  1. 创建一个 class 实现 Jasypt StringEncryptor 并将 StandardPBEStringEncryptor 作为属性保存。实现的加密和解密方法调用 StandardPBEStringEncryptor 的加密和解密方法,但捕获抛出的任何异常。

    • 这是我在原回答中给出的解决方案。
    • 这很危险,如果加密值无法解密,则不应忽略。捆绑包不应启动,以防止例如您的数据库帐户被锁定。
  2. 在将值传递给占位符解析器之前手动解密值。

    • 您可以创建一个配置服务,在其中编译来自各种来源的所有配置,手动解密所有加密值,然后将属性公开为 OSGi 服务以跨包共享。
    • 我放弃了这个设计,它基本上是重新实现了 Karaf 原生提供的 ConfigurationAdmin 服务(加上 Karaf 不提供的解密功能),只是不如那个 Karaf提供,因为它无法检测应用程序配置何时更改。
  3. 在运行时解密值。
    • 也不喜欢这个,要求您的应用程序知道哪些应用程序属性需要加密。

我已经通过我们的支持合同向 Redhat 提出了支持请求,如果有任何进展,我会及时通知您。

原答案:

我想我明白了。根据 camel 文档,在 blueprint camel 中能够检测到存在蓝图占位符解析器,并尝试使用它来解析其属性。

这个问题是它不关心占位符前缀和后缀是什么,它只是继续使用它而不管。 Jasypt 占位符解析器已被设计为仅当占位符前缀为 "ENC(" 且后缀为“)”时才会调用它,请记住 Camel dosent 关心这一点。 Camel 将其未解析的属性传递给 Jasypt 属性 解析器,解析器当然会尝试解密它们。因为它们没有加密,所以会抛出异常。

为了解决这个问题,我创建了一个实现 Jasypt StringEncryptor 的自定义加密器。自定义加密器包含 StandardPBEStingEncryptor 的一个实例,并使用它来进行实际的加密/解密。关键区别在于异常会被捕获和忽略,因此如果在尝试解密未加密的 camel 属性 时抛出异常,则它会被忽略并且应用程序将照常运行。

Java Class:

package uk.co.test;

import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomEncryptor implements StringEncryptor {

    private StandardPBEStringEncryptor encryptor;
    private static final Logger LOG = LoggerFactory.getLogger(CustomEncryptor.class);

    public CustomEncryptor(String password) {
        encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(password);
    }

    @Override
    public String decrypt(String value) {

        String ret = null;

        try {
            ret = encryptor.decrypt(value);
        } catch (Exception e) {
            LOG.error("Failed to decrypt value.");
        }

        return ret;
    }

    @Override
    public String encrypt(String value) {

        String ret = null;

        try {
            ret = encryptor.encrypt(value);
        } catch (Exception e) {
            LOG.error("Failed to encrypt value.");
        }

        return ret;
    }

    public StandardPBEStringEncryptor getEncryptor() {
        return encryptor;
    }

    public void setEncryptor(StandardPBEStringEncryptor encryptor) {
        this.encryptor = encryptor;
    }

}

蓝图配置:

<enc:property-placeholder>
    <enc:encryptor class="uk.co.test.CustomEncryptor">
        <argument value="myPass" />
    </enc:encryptor>
</enc:property-placeholder>