Шифрование токена имени пользователя с помощью 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>