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[]