Шифрование токена имени пользователя с помощью Apache CXF

Приветствую добрых людей.

У меня есть мыльный веб-сервис, который я хочу использовать. Я создал небольшой проект для имитации того, что требуется в реальном проекте, особенно с использованием шифрования токена имени пользователя.

Ниже приведены шаги по шифрованию пароля на стороне клиента, как указано ниже:

  • Напишите незашифрованное значение пароля.
  • Затем зашифруйте блок данных, созданный на шаге 1, с помощью открытой части сертификата ключа пароля. Используйте алгоритм RSA, используйте дополнение PKCS #1.5 (не OAEP) и добавьте результат в зашифрованный поток - он станет зашифрованным паролем, который передается через API.
  • Преобразуйте полученный зашифрованный байтовый массив в строку, используя кодировку base64. Представьте эту закодированную base64 строку в запросе API в качестве значения инициатора SecurityCredential.
  • Пароль должен быть зашифрован с помощью открытого ключа из сертификата X509, выданного Инициатору специально для этой цели.

До сих пор мне удалось создать клиента и сервер, и я могу отправить запрос и получить ответ.

Я также могу защитить веб-службу, передав токен имени пользователя с паролем в виде простого текста в классе ClientPasswordCallback и проверив эти учетные данные в классе ServerPasswordCallback.

Я пошел дальше и в отдельном запросе зашифровал часть тела сообщения, используя wss4j, RSA, X509, в результате чего у меня есть открытый ключ, сохраненный в clientKey.jks, и закрытый ключ, сохраненный в privateKey.jks, и предоставив соответствующие пароли в клиенте и Обработчики обратного вызова пароля сервера Мне удалось зашифровать часть тела на клиенте и расшифровать ее на сервере.

Проблема: Основная проблема, с которой я сталкиваюсь, - это объединение двух вышеупомянутых шагов в одном запросе, так что с помощью открытого ключа я могу зашифровать пароль в токене имени пользователя и расшифровать его на стороне сервера с помощью частного ключ.

Примечание: я сгенерировал ключи для тестирования с помощью инструмента keygen, который поставляется с jdk.

Я предполагаю, что в классе ClientPasswordCallback будет два пароля, один для хранилища ключей clientKey.jks, а другой - пароль, который необходимо зашифровать.

Это то, что я смог заархивировать до сих пор:

Сторона клиента

Класс TestMathUtility

public static void main(String[] args) {

    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

    // Use the URL defined in the soap address portion of the WSDL
    factory.setAddress("http://localhost:8080/MathUtility/services/MathUtilityPort");

    // Utilize the class which was auto-generated by Apache CXF wsdl2java
    factory.setServiceClass(MathUtility.class);

    Object client = factory.create();

    // Adding Logging Interceptors
    LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
    loggingOutInterceptor.setPrettyLogging(true);
    ClientProxy.getClient(client).getOutInterceptors().add(loggingOutInterceptor);

    LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
    loggingInInterceptor.setPrettyLogging(true);
    ClientProxy.getClient(client).getInInterceptors().add(loggingInInterceptor);

    // Set up WS-Security Encryption
    // Reference: https://ws.apache.org/wss4j/using.html
    Map<String, Object> props = new HashMap<String, Object>();
    props.put(WSHandlerConstants.USER, "testkey");
    props.put(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
    props.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
    props.put(WSHandlerConstants.ENC_PROP_FILE, "clientKeystore.properties");
    props.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
    props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());

    WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);

    ClientProxy.getClient(client).getOutInterceptors().add(wss4jOut);

    try {

        // Call the Web Service to perform an operation
        int response = ((MathUtility)client).addIntegers(5, 10);

        System.out.println("Response we've got ========= "+response);

      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      }

}

Класс ClientPasswordCallback

public class ClientPasswordCallback implements CallbackHandler {

@Override
public void handle(Callback[] callbacks) throws IOException,
        UnsupportedCallbackException {

    WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

    // set the password for our message.
    pc.setPassword("clientstorepass");

}

}

Серверная сторона

Класс MathUtility

@WebService(targetNamespace = "http://utility.math.com/", portName =      "MathUtilityPort", serviceName = "MathUtilityService")
public class MathUtility {

public int addIntegers(int firstNum, int secondNum) {
    return firstNum + secondNum;
}

public int factorial(int n) {
    int result = 1;

    for (int i = 1; i <= n; i++) {
        result = result * i;
    }

    return result;
}
}

Класс ServerPasswordCallback

public class ServerPasswordCallback implements CallbackHandler {

@Override
public void handle(Callback[] arg0) throws IOException,
        UnsupportedCallbackException {

    WSPasswordCallback pc = (WSPasswordCallback) arg0[0];

    // set the password for our message.
    pc.setPassword("storepass");

}

}

CXF-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="myPasswordCallback" class="com.math.utility.security.ServerPasswordCallback"/>

<jaxws:endpoint xmlns:tns="http://utility.math.com/" id="mathutility"
    implementor="com.math.utility.MathUtility" wsdlLocation="wsdl/mathutility.wsdl"
    endpointName="tns:MathUtilityPort" serviceName="tns:MathUtilityService"
    address="/MathUtilityPort">
    <jaxws:features>
        <bean class="org.apache.cxf.feature.LoggingFeature" />
    </jaxws:features>

        <jaxws:inInterceptors>
          <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
             <constructor-arg>
                <map>
                   <entry key="user" value="testkey"/>
                   <entry key="action" value="Encrypt"/>
                   <entry key="passwordType" value="PasswordText"/>
                   <entry key="decryptionParts" value="{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
                   <entry key="decryptionPropFile" value="serverKeystore.properties"/>
                   <entry key="passwordCallbackRef">
                      <ref bean="myPasswordCallback"/>
                   </entry>
                </map>
             </constructor-arg>
          </bean>

       </jaxws:inInterceptors>

</jaxws:endpoint>

файл clientKeyStore.properties той же структуры используется на стороне сервера

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientkeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=clientstorepass
org.apache.ws.security.crypto.merlin.keystore.type=jks

Используемые файлы.jks не были предоставлены

NB Я не использую весну.

2 ответа

Если вы хотите создать собственный дайджест, вы можете переопределить метод verifyCustomPassword(UsernameToken usernameToken, данные RequestData) в UsernameTokenValidator.

Чтобы подключить его к вашему веб-сервису, взгляните на мой ответ на другой SO-вопрос. Основы этих ответов:

<property name="wssConfig">
        <ref bean="usernameTokenWssConfig"/>
</property>

И добавьте ссылочный класс в вашу кодовую базу:

@Component("usernameTokenWssConfig")
public class UsernameTokenWssConfig extends WSSConfig {
    public UsernameTokenWssConfig() {
        setValidator(WSSecurityEngine.USERNAME_TOKEN, new CustomUsernameTokenValidator());
        setRequiredPasswordType(WSConstants.CUSTOM_TOKEN );
    }
}

Вот как я сделал шифрование UT с использованием частей шифрования

outProps.put(WSHandlerConstants.ENCRYPTION_PARTS,"{Content}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}UsernameToken");

Чтобы зашифровать токен имени пользователя с частями шифрования. Добавьте пространство имен заголовка SOAP во вторую фигурную скобку.

У меня есть зашифрованный UT, как показано ниже (я зашифровал только контент, только вы можете использовать элемент, если хотите)

      <wsse:UsernameToken
                wsu:Id="UsernameToken-99bea96d-c6ef-444c-aa8a-ec807f58aa0c">
                <xenc:EncryptedData
                    xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
                    Id="ED-59f84d2b-3195-436f-b8f4-513fea23c00a"
                    Type="http://www.w3.org/2001/04/xmlenc#Content">
                    <xenc:EncryptionMethod
                        Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
                    <ds:KeyInfo
                        xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                        <wsse:SecurityTokenReference
                            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                            xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
                            wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
                            <wsse:Reference
                                URI="#EK-748c3d27-f6be-4b81-b864-87bc6457e247" />
                        </wsse:SecurityTokenReference>
                    </ds:KeyInfo>
                    <xenc:CipherData>
                        <xenc:CipherValue>wSZsu9LR6q9fpUPYYF5GSA7T/3iZWMd0cB/80Z33DThzCB0kqnupGETVmGfVQheGUc3O+/B4X7i70aMTyOo5u0fIqa4kwrlKZBe9he359mpgakKgC4wOb65sDThT1fH4PvY6TSBjIOJ0T5jIyt1pGwacRLzmvFxxHxr3qfAOf27LLGJ0P0eAKchE19nAkfP+Tc2GbAkcxi/4SDQ7bBWVaveRgSET0dpheooBGORtt4VJ/dyMwogupAyJKoiqe3RFRCvsmK/UtkVGQYh/W14ei/s7G3mVAch8fQZXCS8jcEaqzkDaNzrZo8+IjJFgrPQY23g3fp57QXIDB84NNUhsm7NHXMNfAq7x97kng+Qwke6uqHcMPjGI9boKw/wZmhipYstFzUpOpF86W9FwcJPyTFR58jvdnX5OGJ1wFbFdI9cAjWdncIEmnOTl69pKRmGmbJYj7Ie43q+eNH/1+2RawBRhZG43VLZL5C7ydFu0xJ2DsD4nacvDfH0i8tcMCHyHkWf2po9Y/dBtS2kWAxfNxWQNvI1BceumsMvpSzK7WjXPJ/vaKlMoSQJtsBxg9RhA
                        </xenc:CipherValue>
                    </xenc:CipherData>
                </xenc:EncryptedData>
            </wsse:UsernameToken>
Другие вопросы по тегам