You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/07/02 15:25:50 UTC
[3/3] syncope git commit: [SYNCOPE-1329] Flexible configuration
allowing both HMAC and RS_SHA for JWS
[SYNCOPE-1329] Flexible configuration allowing both HMAC and RS_SHA for JWS
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/b06f0a6f
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/b06f0a6f
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/b06f0a6f
Branch: refs/heads/2_0_X
Commit: b06f0a6f1ded8f7b7dda7e1d10dfa7d2a739945f
Parents: 0497110
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Jul 2 14:56:41 2018 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Jul 2 17:25:39 2018 +0200
----------------------------------------------------------------------
.../java/data/AccessTokenDataBinderImpl.java | 4 +-
.../src/test/resources/provisioningTest.xml | 24 +++---
.../security/DefaultCredentialChecker.java | 1 -
.../spring/security/SyncopeJWTSSOProvider.java | 7 +-
.../jws/AccessTokenJwsSignatureProvider.java | 91 ++++++++++++++++++++
.../jws/AccessTokenJwsSignatureVerifier.java | 91 ++++++++++++++++++++
.../src/main/resources/security.properties | 6 +-
.../src/main/resources/securityContext.xml | 18 ++--
.../apache/syncope/core/logic/SAML2SPLogic.java | 2 +-
.../core/reference/CustomJWTSSOProvider.java | 1 -
.../org/apache/syncope/fit/AbstractITCase.java | 8 +-
.../org/apache/syncope/fit/core/JWTITCase.java | 91 ++++++++++++--------
.../systemadministration/jws.adoc | 78 +++++++++++++++++
.../systemadministration.adoc | 2 +
14 files changed, 349 insertions(+), 75 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
index 0b2e4b6..327b396 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
@@ -28,7 +28,6 @@ import org.apache.cxf.rs.security.jose.common.JoseType;
import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.syncope.common.lib.to.AccessTokenTO;
@@ -39,6 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
import org.apache.syncope.core.spring.BeanUtils;
import org.apache.syncope.core.spring.security.DefaultCredentialChecker;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -54,7 +54,7 @@ public class AccessTokenDataBinderImpl implements AccessTokenDataBinder {
private String jwtIssuer;
@Autowired
- private JwsSignatureProvider jwsSignatureProvider;
+ private AccessTokenJwsSignatureProvider jwsSignatureProvider;
@Autowired
private AccessTokenDAO accessTokenDAO;
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/provisioning-java/src/test/resources/provisioningTest.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/resources/provisioningTest.xml b/core/provisioning-java/src/test/resources/provisioningTest.xml
index e3c1dd2..903d2c7 100644
--- a/core/provisioning-java/src/test/resources/provisioningTest.xml
+++ b/core/provisioning-java/src/test/resources/provisioningTest.xml
@@ -44,22 +44,20 @@ under the License.
<bean id="jwsKey" class="java.lang.String">
<constructor-arg value="${jwsKey}"/>
</bean>
- <bean id="jwsSignatureVerifier" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier">
- <constructor-arg value="#{jwsKey.getBytes()}" index="0"/>
- <constructor-arg index="1">
- <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">HS512</value>
- </constructor-arg>
+ <bean id="accessTokenJwsSignatureVerifier"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="${jwsKey}"/>
</bean>
- <bean id="jwsSignatureProvider" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider">
- <constructor-arg value="#{jwsKey.getBytes()}" index="0"/>
- <constructor-arg index="1">
- <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">HS512</value>
- </constructor-arg>
+ <bean id="accessTokenJwsSignatureProvider"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="${jwsKey}"/>
</bean>
<bean id="credentialChecker" class="org.apache.syncope.core.spring.security.DefaultCredentialChecker">
- <constructor-arg value="${jwsKey}" index="0"/>
- <constructor-arg value="${adminPassword}" index="1"/>
- <constructor-arg value="${anonymousKey}" index="2"/>
+ <constructor-arg value="${jwsKey}" index="0"/>
+ <constructor-arg value="${adminPassword}" index="1"/>
+ <constructor-arg value="${anonymousKey}" index="2"/>
</bean>
</beans>
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
index a63c588..7e7b65f 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
@@ -66,5 +66,4 @@ public class DefaultCredentialChecker {
+ "This must be changed to avoid a security breach!");
}
}
-
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
index 3a20289..08715e1 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
@@ -25,7 +25,6 @@ import javax.annotation.Resource;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -34,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.AccessToken;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -51,8 +51,8 @@ public class SyncopeJWTSSOProvider implements JWTSSOProvider {
@Resource(name = "jwtIssuer")
private String jwtIssuer;
- @Resource(name = "syncopeJWTSSOProviderDelegate")
- private JwsSignatureVerifier delegate;
+ @Autowired
+ private AccessTokenJwsSignatureVerifier delegate;
@Autowired
private UserDAO userDAO;
@@ -101,5 +101,4 @@ public class SyncopeJWTSSOProvider implements JWTSSOProvider {
return Pair.of(user, authorities);
}
-
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
new file mode 100644
index 0000000..383cf89
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.spring.security.jws;
+
+import java.security.KeyFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsSignature;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
+import org.apache.cxf.rs.security.jose.jws.PrivateKeyJwsSignatureProvider;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.crypto.codec.Base64;
+
+public class AccessTokenJwsSignatureProvider implements JwsSignatureProvider, InitializingBean {
+
+ private SignatureAlgorithm jwsAlgorithm;
+
+ private String jwsKey;
+
+ private JwsSignatureProvider delegate;
+
+ public void setJwsAlgorithm(final SignatureAlgorithm jwsAlgorithm) {
+ this.jwsAlgorithm = jwsAlgorithm;
+ }
+
+ public void setJwsKey(final String jwsKey) {
+ this.jwsKey = jwsKey;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (jwsAlgorithm == null) {
+ throw new IllegalArgumentException("An instance of " + SignatureAlgorithm.class + " is required");
+ }
+
+ if (SignatureAlgorithm.isPublicKeyAlgorithm(jwsAlgorithm)) {
+ if (!jwsAlgorithm.getJwaName().startsWith("RS")) {
+ throw new IllegalArgumentException(jwsAlgorithm.getJavaName() + " not supported.");
+ }
+
+ if (jwsKey == null || jwsKey.indexOf(':') == -1) {
+ throw new IllegalArgumentException("A key pair is required, in the 'private:public' format");
+ }
+
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(
+ Base64.decode(StringUtils.substringBefore(jwsKey, ":").getBytes()));
+ delegate = new PrivateKeyJwsSignatureProvider(kf.generatePrivate(keySpecPKCS8), jwsAlgorithm);
+ } else {
+ if (jwsKey == null) {
+ throw new IllegalArgumentException("A shared key is required");
+ }
+
+ delegate = new HmacJwsSignatureProvider(jwsKey.getBytes(), jwsAlgorithm);
+ }
+ }
+
+ @Override
+ public SignatureAlgorithm getAlgorithm() {
+ return delegate.getAlgorithm();
+ }
+
+ @Override
+ public byte[] sign(final JwsHeaders headers, final byte[] content) {
+ return delegate.sign(headers, content);
+ }
+
+ @Override
+ public JwsSignature createJwsSignature(final JwsHeaders headers) {
+ return delegate.createJwsSignature(headers);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
new file mode 100644
index 0000000..fe8db3e
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.spring.security.jws;
+
+import java.security.KeyFactory;
+import java.security.spec.X509EncodedKeySpec;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
+import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
+import org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.crypto.codec.Base64;
+
+public class AccessTokenJwsSignatureVerifier implements JwsSignatureVerifier, InitializingBean {
+
+ private SignatureAlgorithm jwsAlgorithm;
+
+ private String jwsKey;
+
+ private JwsSignatureVerifier delegate;
+
+ public void setJwsAlgorithm(final SignatureAlgorithm jwsAlgorithm) {
+ this.jwsAlgorithm = jwsAlgorithm;
+ }
+
+ public void setJwsKey(final String jwsKey) {
+ this.jwsKey = jwsKey;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (jwsAlgorithm == null) {
+ throw new IllegalArgumentException("An instance of " + SignatureAlgorithm.class + " is required");
+ }
+
+ if (SignatureAlgorithm.isPublicKeyAlgorithm(jwsAlgorithm)) {
+ if (!jwsAlgorithm.getJwaName().startsWith("RS")) {
+ throw new IllegalArgumentException(jwsAlgorithm.getJavaName() + " not supported.");
+ }
+
+ if (jwsKey == null || jwsKey.indexOf(':') == -1) {
+ throw new IllegalArgumentException("A key pair is required, in the 'private:public' format");
+ }
+
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(
+ Base64.decode(StringUtils.substringAfter(jwsKey, ":").getBytes()));
+ delegate = new PublicKeyJwsSignatureVerifier(kf.generatePublic(keySpecX509), jwsAlgorithm);
+ } else {
+ if (jwsKey == null) {
+ throw new IllegalArgumentException("A shared key is required");
+ }
+
+ delegate = new HmacJwsSignatureVerifier(jwsKey.getBytes(), jwsAlgorithm);
+ }
+ }
+
+ @Override
+ public SignatureAlgorithm getAlgorithm() {
+ return delegate.getAlgorithm();
+ }
+
+ @Override
+ public boolean verify(final JwsHeaders headers, final String unsignedText, final byte[] signature) {
+ return delegate.verify(headers, unsignedText, signature);
+ }
+
+ @Override
+ public JwsVerificationSignature createJwsVerificationSignature(final JwsHeaders headers) {
+ return delegate.createJwsVerificationSignature(headers);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/resources/security.properties
----------------------------------------------------------------------
diff --git a/core/spring/src/main/resources/security.properties b/core/spring/src/main/resources/security.properties
index 1f68aaf..f44e2f4 100644
--- a/core/spring/src/main/resources/security.properties
+++ b/core/spring/src/main/resources/security.properties
@@ -25,11 +25,9 @@ anonymousKey=${anonymousKey}
secretKey=${secretKey}
-jwsKey=${jwsKey}
jwtIssuer=ApacheSyncope
-
-# Algorthim used to create and authenticate access token
-jwtSignAlgo=HS512
+jwsAlgorithm=HS512
+jwsKey=${jwsKey}
# default for LDAP / RFC2307 SSHA
digester.saltIterations=1
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/core/spring/src/main/resources/securityContext.xml
----------------------------------------------------------------------
diff --git a/core/spring/src/main/resources/securityContext.xml b/core/spring/src/main/resources/securityContext.xml
index 00faa6bf..bec0d12 100644
--- a/core/spring/src/main/resources/securityContext.xml
+++ b/core/spring/src/main/resources/securityContext.xml
@@ -55,17 +55,15 @@ under the License.
<constructor-arg value="${anonymousKey}" index="2"/>
</bean>
- <bean id="syncopeJWTSSOProviderDelegate" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier">
- <constructor-arg value="#{jwsKey.getBytes()}" index="0"/>
- <constructor-arg index="1">
- <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">${jwtSignAlgo}</value>
- </constructor-arg>
+ <bean id="accessTokenJwsSignatureVerifier"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="${jwsKey}"/>
</bean>
- <bean id="jwsSignatureProvider" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider">
- <constructor-arg value="#{jwsKey.getBytes()}" index="0"/>
- <constructor-arg index="1">
- <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">${jwtSignAlgo}</value>
- </constructor-arg>
+ <bean id="accessTokenJwsSignatureProvider"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="${jwsKey}"/>
</bean>
<bean class="${passwordGenerator}"/>
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 943401e..93f961b 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -153,7 +153,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
@Autowired
private SAML2ReaderWriter saml2rw;
- @Resource(name = "syncopeJWTSSOProviderDelegate")
+ @Resource(name = "accessTokenJwsSignatureVerifier")
private JwsSignatureVerifier jwsSignatureVerifier;
private void validateUrl(final String url) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
index 199ba24..fece58b 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
@@ -97,5 +97,4 @@ public class CustomJWTSSOProvider implements JWTSSOProvider {
return null;
}
-
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 5d24bd8..8779b19 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -41,6 +41,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
@@ -182,6 +183,8 @@ public abstract class AbstractITCase {
protected static String JWT_ISSUER;
+ protected static SignatureAlgorithm JWS_ALGORITHM;
+
protected static SyncopeClientFactoryBean clientFactory;
protected static SyncopeClient adminClient;
@@ -249,7 +252,7 @@ public abstract class AbstractITCase {
protected static SAML2SPService saml2SpService;
protected static SAML2IdPService saml2IdPService;
-
+
protected static OIDCClientService oidcClientService;
protected static OIDCProviderService oidcProviderService;
@@ -264,8 +267,9 @@ public abstract class AbstractITCase {
ANONYMOUS_UNAME = props.getProperty("anonymousUser");
ANONYMOUS_KEY = props.getProperty("anonymousKey");
- JWS_KEY = props.getProperty("jwsKey");
JWT_ISSUER = props.getProperty("jwtIssuer");
+ JWS_ALGORITHM = SignatureAlgorithm.valueOf(props.getProperty("jwsAlgorithm"));
+ JWS_KEY = props.getProperty("jwsKey");
} catch (Exception e) {
LOG.error("Could not read secretKey", e);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
index 93ae426..0720961 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
@@ -38,7 +38,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.cxf.rs.security.jose.common.JoseType;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier;
import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
@@ -52,8 +51,12 @@ import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.AccessTokenService;
import org.apache.syncope.common.rest.api.service.UserSelfService;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.core.reference.CustomJWTSSOProvider;
+import org.junit.Assume;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -61,6 +64,25 @@ import org.junit.Test;
*/
public class JWTITCase extends AbstractITCase {
+ private JwsSignatureProvider jwsSignatureProvider;
+
+ private JwsSignatureVerifier jwsSignatureVerifier;
+
+ @Before
+ public void setupVerifier() throws Exception {
+ AccessTokenJwsSignatureProvider atjsp = new AccessTokenJwsSignatureProvider();
+ atjsp.setJwsAlgorithm(JWS_ALGORITHM);
+ atjsp.setJwsKey(JWS_KEY);
+ atjsp.afterPropertiesSet();
+ this.jwsSignatureProvider = atjsp;
+
+ AccessTokenJwsSignatureVerifier atjsv = new AccessTokenJwsSignatureVerifier();
+ atjsv.setJwsAlgorithm(JWS_ALGORITHM);
+ atjsv.setJwsKey(JWS_KEY);
+ atjsv.afterPropertiesSet();
+ this.jwsSignatureVerifier = atjsv;
+ }
+
@Test
public void getJWTToken() throws ParseException {
// Get the token
@@ -75,8 +97,6 @@ public class JWTITCase extends AbstractITCase {
// Validate the signature
JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- JwsSignatureVerifier jwsSignatureVerifier =
- new HmacJwsSignatureVerifier(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
assertTrue(consumer.verifySignatureWith(jwsSignatureVerifier));
Date now = new Date();
@@ -161,12 +181,10 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
String signed = producer.signWith(jwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -202,12 +220,10 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
String signed = producer.signWith(jwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -248,12 +264,10 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime((now.getTime() - 5000L) / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
String signed = producer.signWith(jwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -294,12 +308,10 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime + 60L);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
String signed = producer.signWith(jwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -337,8 +349,8 @@ public class JWTITCase extends AbstractITCase {
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider = new NoneJwsSignatureProvider();
- String signed = producer.signWith(jwsSignatureProvider);
+ JwsSignatureProvider noneJwsSignatureProvider = new NoneJwsSignatureProvider();
+ String signed = producer.signWith(noneJwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService = jwtClient.getService(UserSelfService.class);
@@ -376,12 +388,10 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(JWS_KEY.getBytes(), SignatureAlgorithm.HS512);
String signed = producer.signWith(jwsSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -396,6 +406,8 @@ public class JWTITCase extends AbstractITCase {
@Test
public void thirdPartyToken() throws ParseException {
+ Assume.assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+
// Create a new token
Date now = new Date();
long currentTime = now.getTime() / 1000L;
@@ -412,13 +424,13 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), SignatureAlgorithm.HS512);
- String signed = producer.signWith(jwsSignatureProvider);
+ JwsSignatureProvider customSignatureProvider =
+ new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), JWS_ALGORITHM);
+ String signed = producer.signWith(customSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -429,6 +441,8 @@ public class JWTITCase extends AbstractITCase {
@Test
public void thirdPartyTokenUnknownUser() throws ParseException {
+ Assume.assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+
// Create a new token
Date now = new Date();
long currentTime = now.getTime() / 1000L;
@@ -445,13 +459,13 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), SignatureAlgorithm.HS512);
- String signed = producer.signWith(jwsSignatureProvider);
+ JwsSignatureProvider customSignatureProvider =
+ new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), JWS_ALGORITHM);
+ String signed = producer.signWith(customSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -465,6 +479,8 @@ public class JWTITCase extends AbstractITCase {
@Test
public void thirdPartyTokenUnknownIssuer() throws ParseException {
+ Assume.assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+
// Create a new token
Date now = new Date();
long currentTime = now.getTime() / 1000L;
@@ -481,13 +497,13 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
jwtClaims.setNotBefore(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider =
- new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), SignatureAlgorithm.HS512);
- String signed = producer.signWith(jwsSignatureProvider);
+ JwsSignatureProvider customSignatureProvider =
+ new HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(), JWS_ALGORITHM);
+ String signed = producer.signWith(customSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -501,6 +517,8 @@ public class JWTITCase extends AbstractITCase {
@Test
public void thirdPartyTokenBadSignature() throws ParseException {
+ Assume.assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+
// Create a new token
Date now = new Date();
@@ -516,13 +534,13 @@ public class JWTITCase extends AbstractITCase {
jwtClaims.setExpiryTime(expiry.getTime().getTime());
jwtClaims.setNotBefore(now.getTime());
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+ JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
- JwsSignatureProvider jwsSignatureProvider = new HmacJwsSignatureProvider(
- (CustomJWTSSOProvider.CUSTOM_KEY + "_").getBytes(), SignatureAlgorithm.HS512);
- String signed = producer.signWith(jwsSignatureProvider);
+ JwsSignatureProvider customSignatureProvider =
+ new HmacJwsSignatureProvider((CustomJWTSSOProvider.CUSTOM_KEY + "_").getBytes(), JWS_ALGORITHM);
+ String signed = producer.signWith(customSignatureProvider);
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -533,5 +551,4 @@ public class JWTITCase extends AbstractITCase {
// expected
}
}
-
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/jws.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/jws.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/jws.adoc
new file mode 100644
index 0000000..47bdbe7
--- /dev/null
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/jws.adoc
@@ -0,0 +1,78 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+==== Control JWT signature
+
+As explained <<rest-authentication-and-authorization,above>>, the authentication process generates, in case of success,
+a unique signed JWT (JSON Web Token). +
+Such JWT values are signed by Apache Syncope according to the https://tools.ietf.org/html/rfc7515[JWS^]
+(JSON Web Signature) specification.
+
+[[jws-hmac]]
+===== Hash-based Message Authentication Code
+
+This is the default configuration, where Core and clients posses a shared secret, configured under `security.properties`
+as the `jwsKey` property value.
+
+.Default JWS configuration
+====
+[source,properties]
+----
+jwsAlgorithm=HS512 // <1>
+jwsKey=ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f // <2>
+----
+<1> Valid values are `HS256`, `HS384` and `HS512`
+<2> Any alphanumeric value satisfying the https://tools.ietf.org/html/rfc7518#section-3.2[length requirement^] can be
+used
+====
+
+
+[[jws-rsa]]
+===== RSA Public-Key Cryptography
+
+This configuration requires to specify a key pair: the former key value, said _private_, must be shared with clients,
+while the latter key value, said _public_, must be kept secret for internal Core usage.
+
+The commands below will generate the required key pair via OpenSSL and format their values for usage with
+`security.properties`:
+
+[source,bash]
+----
+$ openssl genrsa -out private_key.pem 2048
+$ openssl pkcs8 -topk8 -in private_key.pem -inform pem -out jws.privateKey -outform pem -nocrypt
+$ openssl rsa -pubout -in private_key.pem -out jws.publicKey
+$ echo `sed '1d;$d' jws.privateKey | awk '{printf "%s", $0}'`:`sed '1d;$d' jws.publicKey | awk '{printf "%s", $0}'`
+----
+
+.JWS configuration with RSA PKCS#1
+====
+[source,properties]
+----
+jwsAlgorithm=RS512 // <1>
+jwsKey=MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCdXTaAPRoIAvWjm5MskNtcGakkME4HEhZ8oQ2J8XNU29ZT7Qq5TP769/O8OH5Pb56mPULswYSocycrAARPzjAKpxr+YN7w2/zo5MsBRZsASgpCxnCeYLCWtJzmzY/YYlAHdsu3jj/4wuAcYozR1xE5e2gEj0BQ6Xz7NELhceEZpbXIeKSDolLdCKrVZ1vdD0q/HdjY2qeBACqeG8yYXsj2MiAMJY6df80ZCqpHkcD9mhfzqUo5EcWCD7XzcOJQRNUKkBEObemq//tt5NHFbWnBeGeTJBcyXV7Uqqbjnd6hwBBS1d6usAagGQ4RWDHPBMk02BdEFyrZjgJXM1C1iU/9AgMBAAECggEBAJpbnaNKzCcBqCuU5ld3vARqk1QRIeijoHUdkWc29TdO8LygLr22vgI1h9qf255V0dwlCWmtJVAKrGfse05A5TT912egY+8FCt7z1gFoYnN1LP11I3DnTTB299UZ3DiXrwKzT368xRlhJm4RaSpIePfWiiC215LGhTbve48iongBXzkpzFYe1SCV1FmNl5Px6FE3C9GcTrFpe+rqVcIVrTLZ95+JDF4/YLgTRccW8V/YO+4OtqUo+vt8tckDGhrHrfwgTo53kxDQttecB4AryDg1eUe8vPMx1+yJz8VFwx0yaUa5fqEYlxPehRQiVJi0+YMosRqKtcm1mLxoGcwSyo0CgYEAynhB/FM9DnARwg/PsE/AuXVpXlxPU5F+shpYX2sF3rItTD4EWFr/glo26LT/MLw2ckNkLT11yAWdR8hAzVZ48Ly3Ur8Fi88iInLPEixunBIsPcR3dI2UoI9dswnTM+H/Z83yQ16VWGjtE3437LWSXBHEw/am9W9pArEunt3TQz8CgYEAxvgS7BAokIqASi0zBpmyogRVHGs0eC3mMWLG+t5VXJ5M1z1pV9dOuInnI29wJq
BscefueOPcT6mNJngW/kHlcGGOxij+hRUnAdVltTod4CJ3Q/IyM6h/FzunEeumZyZ1BW3G5KTcpegcBquUW6impyJbnUvKV4p9rpLTEBooKcMCgYEAhB1skUWPdbhTHhpLH3UrANlIZDY/3Pv3fCgMulaPgf0p6bIeC7l1OI29fqN8UUS/Elg/KfYMwPRI6OoWvuZKDGxYAzp6V/xU/b2EuQsdMeH51GQ6vmcUMKDcN1OV6SjzC70q9CLnuMTezfVycJcaZdGCX4y27ThBgWw0S53bmOkCgYAdCHfiYF068irUKBJJBUZuo8kzk2UdoDz1ud8lHipAkIzP35MukSlYfi7vGcS4rjIE0P4YP8+XBDungGCCi2UKaAHoYnT5QGPnvZbQwgE4Am96x62RoiWhYz/2uncWmCL9Ps6F8JSN1Pe59XF5int+6eGKa1PEQF4kiiIoOFjh9wKBgG6XXGl84fBaOaTsCPu+oQcAAp1GzweSy4l1Y1L71YvbxU1bs5338vgiH5OeUA4d5w0Ei9d/bSw0PWV4aACWWGGclLhzv8ia6bEWqt0TskUiUJVzgTXWp3ojpsP/QE36Ty+uWWqckBXv6dnEXEgrLqzbA6qTAohSSFjV4FAjxBxa:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnV02gD0aCAL1o5uTLJDbXBmpJDBOBxIWfKENifFzVNvWU+0KuUz++vfzvDh+T2+epj1C7MGEqHMnKwAET84wCqca/mDe8Nv86OTLAUWbAEoKQsZwnmCwlrSc5s2P2GJQB3bLt44/+MLgHGKM0dcROXtoBI9AUOl8+zRC4XHhGaW1yHikg6JS3Qiq1Wdb3Q9Kvx3Y2NqngQAqnhvMmF7I9jIgDCWOnX/NGQqqR5HA/ZoX86lKORHFgg+183DiUETVCpARDm3pqv/7beTRxW1pwXhnkyQXMl1e1Kqm453eocAQUtXerrAGoBkOEVgxzw
TJNNgXRBcq2Y4CVzNQtYlP/QIDAQAB // <2>
+----
+<1> Valid values are `RS256`, `RS384` and `RS512`
+<2> Value is obtained by the commands above; the public key value is the string after the `:` sign, e.g.
+```
+QB3bLt44/+MLgHGKM0dcROXtoBI9AUOl8+zRC4XHhGaW1yHikg6JS3Qiq1Wdb3Q9Kvx3Y2NqngQAqnhvMmF7I9jIgDCWOnX/NGQqqR5HA/ZoX86lKORHFgg+183DiUETVCpARDm3pqv/7beTRxW1pwXhnkyQXMl1e1Kqm453eocAQUtXerrAGoBkOEVgxzwTJNNgXRBcq2Y4CVzNQtYlP/QIDAQAB
+```
+====
+
+[TIP]
+Longer RSA keys offer stronger protection against cracking. The JWS specification suggests at least 2048 bits.
+Please consider that higher CPU usage is involved with longer keys.
http://git-wip-us.apache.org/repos/asf/syncope/blob/b06f0a6f/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/systemadministration.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/systemadministration.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/systemadministration.adoc
index 2c12884..3df2699 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/systemadministration.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/systemadministration.adoc
@@ -74,4 +74,6 @@ include::emailconfiguration.adoc[]
include::keystore.adoc[]
+include::jws.adoc[]
+
include::configurationparameters.adoc[]