Hitachi Vantara Pentaho Community Wiki
Child pages
  • Authentication via X.509 Client Certificate
Skip to end of metadata
Go to start of metadata

X509 Authentication

This document describes how to enable authentication via X.509 certificates. It supplements the official Spring Security documentation on this subject. This kind of authentication takes place when (1) using HTTPS and (2) the server requires a client certificate. Normally when an HTTPS connection is established, only the server provides a certificate. Additionally, the server can require the client to also present a certificate. This is called mutual authentication, or two way SSL authentication.

Prerequisites

This document applies to the Pentaho BI Server, version 2.0 and higher.

The instructions below assume three entities:

Entity

Description

certificate authority (CA)

In this example, you will play the role of CA. In a real life scenario, a company like VeriSign would serve as the CA.

server

This is a server such a Apache Tomcat. It will both present a certificate of its own and request a certificate from the client.

client

This is a browser such as Mozilla Firefox. It will be configured to present a client certificate when prompted.

Instructions

  1. Prepare the certificate authority (CA) certificate.
    1. Generate the CA private key.
    2. Generate the self-signed certificate for the CA using the CA private key.
  2. Prepare the server certificate.
    1. Generate the server private key.
    2. Generate the server certificate signing request (CSR).
    3. Sign the server CSR using the CA private key and CA certificate.
  3. Prepare the client certificate.
    1. Generate the client private key.
    2. Generate the client CSR.
    3. Sign the client CSR using the CA private key and CA certificate.
  4. Import the CA certificate into the keystore (e.g. cacerts) containing the root certificates of certificate authorities.
    1. Export the CA private key and CA certificate into a format suitable for the JVM keystore.
    2. Import the CA private key and CA certificate into a temporary keystore.
    3. Export the CA certificate out of the temporary keystore.
    4. Import the CA certificate into the cacerts file.
  5. Import the server private key and server certificate into the keystore to be used by the server.
    1. Export the server private key and server certificate into a format suitable for the server keystore.
    2. Import the server private key and server certificate into the server keystore.
  6. Edit the server config to enable HTTPS and to use the server keystore.
  7. Import the client private key and client certificate into the browser.
    1. Export the client private key and client certificate into a format suitable for the browser.
    2. Import the client private key and client certificate into the browser.
  8. Modify the Pentaho BI Server security configuration to handle client certificates.

Results

After completing these instructions, the Pentaho BI Server will authenticate via client certificates. If a certificate is not supplied by the user or if the certificate is invalid, the Pentaho BI Server will respond with an HTTP status 403 (Forbidden).

Examples

Example using OpenSSL

The example below assumes the presence of the OpenSSL toolkit. Most Linux distributions come with OpenSSL. For Windows, there are a pre-compiled binaries.

Prepare the certificate authority (CA) certificate.

# Generate the CA private key.
# You will be prompted for a key password.
openssl genrsa -des3 -out ca.key 4096

# Generate the self-signed certificate for the CA using the CA private key.
openssl req -new -x509 -days 1825 -key myca.key -out myca.crt

Prepare the server certificate.

# Generate the server private key.
# You will be prompted for a key password.
openssl genrsa -des3 -out server.key 4096

# Generate the server certificate signing request (CSR).
openssl req -new -key server.key -out server.csr

# Sign the server CSR using the CA private key and CA certificate.
openssl x509 -req -days 1825 -in server.csr -CA myca.crt -CAkey myca.key -set_serial 01 -out server.crt

Prepare the client certificate.

# Generate the client private key.
# You will be prompted for a key password.
openssl genrsa -des3 -out me.key 4096

# Generate the client CSR.
openssl req -new -key me.key -out me.csr

# Sign the client CSR using the CA private key and CA certificate.
openssl x509 -req -days 1825 -in me.csr -CA myca.crt -CAkey myca.key -set_serial 02 -out me.crt

Import the CA certificate into the keystore containing the root certificates of certificate authorities.

# Export the CA private key and CA certificate into a format suitable for the JVM keystore.
openssl pkcs8 -topk8 -nocrypt -in myca.key -out myca.key.der -outform der
openssl x509 -in myca.crt -out myca.crt.der -outform der

# Import the CA private key and CA certificate into a temporary keystore
# The command below uses the attached Java class.
java -Dkeystore=MyCAKeystore -jar ImportKey.jar myca.key.der myca.crt.der

# Export the CA certificate out of the temporary keystore.
keytool -export -alias importkey -file myca.crt.jks -keystore MyCAKeystore

# Import the CA certificate into the cacerts file.
sudo keytool -import -alias mypentahoca -keystore cacerts -trustcacerts -file ~/tmp/myca.crt.jks

Import the server private key and server certificate into the keystore to be used by the server.

# Export the server private key and server certificate into a format suitable for the server keystore.
openssl pkcs8 -topk8 -nocrypt -in server.key -out server.key.der -outform der
openssl x509 -in server.crt -out server.crt.der -outform der

# Import the server private key and server certificate into the server keystore.
# The command below uses the attached Java class.
java -Dkeystore=TomcatKeystore -Dkeypass=changeit -jar ImportKey.jar server.key.der server.crt.der tomcat

Edit the server config to enable HTTPS and to use the server keystore.

# Edit the server config to enable HTTPS and to use the server keystore.
# Note: clientAuth="false" tells Tomcat not to prompt for a client certificate. Normally this is false, however
# you may wish to set it to true to force a prompt. This would be appropriate if clients are already submitting
# their client cert anyway.
<Connector port="8443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS" keystoreFile="/home/joe/tmp/TomcatKeystore" />

Import the client private key and client certificate into the browser.

# Export the client private key and client certificate into a format suitable for the browser.
# In this case, Mozilla Firefox requires PKCS12 format.
openssl pkcs12 -export -out me.pkcs12 -in me.crt -inkey me.key

Now import the client private key and client certificate (bundled in a single file in the last step) into the browser.

In Firefox:

  1. Click Edit | Preferences.
  2. Click Advanced | Encryption.
  3. Click View Certificates.
  4. Click Your Certificates.
  5. Click Import...
  6. Choose the me.pkcs12 file.
  7. Enter the export password.
  8. Click OK and then click Close.

Modify the Pentaho BI Server security configuration to handle client certificates.

Note: The applicationContext-spring-security.xml below has been modified such that the only authentication is X509. In other words, there will be no login page if the user fails to submit a X509 client certificate. In the event that an X509 certificate is not found, an HTTP status 403 (Forbidden) will be returned to the client.

Warning: Note that the end-of-line backslashes that occur in the excerpt below are present for formatting purposes only and should not be present in the actual file.

applicationContext-spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

  <!-- ======================== FILTER CHAIN ======================= -->
  <!--
    if you wish to use channel security, add "channelProcessingFilter," in front of
    "httpSessionContextIntegrationFilter" in the list below
  -->
  <bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
    <property name="filterInvocationDefinitionSource">
      <value><![CDATA[CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
  PATTERN_TYPE_APACHE_ANT
  /**=channelProcessingFilter,securityContextHolderAwareRequestFilter,httpSessionContextIntegrationFilter, \
  httpSessionReuseDetectionFilter,logoutFilter,x509ProcessingFilter,anonymousProcessingFilter, \
  pentahoSecurityStartupFilter,exceptionTranslationFilter,filterInvocationInterceptor]]>
      </value>
    </property>
  </bean>

	<!-- ======================== AUTHENTICATION ======================= -->
  <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
    <property name="providers">
      <list>
        <ref local="x509AuthenticationProvider" />
        <ref local="anonymousAuthenticationProvider" />
      </list>
    </property>
  </bean>

	<!-- Automatically receives AuthenticationEvent messages -->
  <bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" />
  <bean id="pentahoSecurityStartupFilter" class="org.pentaho.platform.web.http.security.SecurityStartupFilter" />
  <bean id="anonymousProcessingFilter" class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
    <property name="key" value="foobar" />
    <property name="userAttribute" value="anonymousUser,Anonymous" />
  </bean>
  <bean id="anonymousAuthenticationProvider" class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
    <property name="key" value="foobar" />
  </bean>
  <bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
    <property name="context" value="org.springframework.security.context.SecurityContextImpl" />
  </bean>
  <bean id="x509AuthenticationProvider" class="org.springframework.security.providers.x509.X509AuthenticationProvider">
    <property name="x509AuthoritiesPopulator">
      <ref local="x509AuthoritiesPopulator" />
    </property>
  </bean>
  <bean id="x509AuthoritiesPopulator" class="org.springframework.security.providers.x509.populator.DaoX509AuthoritiesPopulator">
    <property name="userDetailsService">
      <ref bean="userDetailsService" />
    </property>
  </bean>
  <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
    <constructor-arg value="/index.jsp" />
    <constructor-arg>
      <list>
        <bean class="org.pentaho.platform.web.http.security.PentahoLogoutHandler" />
        <bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" />
      </list>
    </constructor-arg>
    <property name="filterProcessesUrl" value="/Logout" />
  </bean>
  <bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter" />
  <bean id="httpSessionReuseDetectionFilter" class="org.pentaho.platform.web.http.security.HttpSessionReuseDetectionFilter">
    <property name="filterProcessesUrl" value="/j_spring_security_check" />
    <property name="sessionReuseDetectedUrl" value="/Login?login_error=2" />
  </bean>

  <!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== -->
  <!-- Enabled by default for X.509 (obviously) -->
  <bean id="channelProcessingFilter" class="org.springframework.security.securechannel.ChannelProcessingFilter">
    <property name="channelDecisionManager">
      <ref local="channelDecisionManager" />
    </property>
    <property name="filterInvocationDefinitionSource">
      <value>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
  \A.*\Z=REQUIRES_SECURE_CHANNEL</value>
    </property>
  </bean>
  <bean id="channelDecisionManager" class="org.springframework.security.securechannel.ChannelDecisionManagerImpl">
    <property name="channelProcessors">
      <list>
        <ref local="secureChannelProcessor" />
        <ref local="insecureChannelProcessor" />
      </list>
    </property>
  </bean>
  <bean id="secureChannelProcessor" class="org.springframework.security.securechannel.SecureChannelProcessor" />
  <bean id="insecureChannelProcessor" class="org.springframework.security.securechannel.InsecureChannelProcessor" />

  <!-- ===================== HTTP REQUEST SECURITY ==================== -->
  <bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint">
      <ref local="x509ProcessingFilterEntryPoint" />
    </property>
    <property name="accessDeniedHandler">
      <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl" />
    </property>
  </bean>
  <bean id="x509ProcessingFilter" class="org.springframework.security.ui.x509.X509ProcessingFilter">
    <property name="authenticationManager">
      <ref local="authenticationManager" />
    </property>
  </bean>
  <bean id="x509ProcessingFilterEntryPoint" class="org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint">
  </bean>
  <bean id="httpRequestAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
    <property name="allowIfAllAbstainDecisions" value="false" />
    <property name="decisionVoters">
      <list>
        <ref bean="roleVoter" />
      </list>
    </property>
  </bean>
  <bean id="filterInvocationInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
    <property name="authenticationManager">
      <ref local="authenticationManager" />
    </property>
    <property name="accessDecisionManager">
      <ref local="httpRequestAccessDecisionManager" />
    </property>
    <property name="objectDefinitionSource">
      <value>
        <![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/public/.*\Z=Anonymous,Authenticated
\A/login.*\Z=Anonymous,Authenticated
\A/getmondrianmodel.*\Z=Anonymous,Authenticated
\A/getimage.*\Z=Anonymous,Authenticated
\A/getresource.*\Z=Anonymous,Authenticated
\A/admin.*\Z=Admin
\A/auditreport.*\Z=Admin
\A/auditreportlist.*\Z=Admin
\A/versioncontrol.*\Z=Admin
\A/propertieseditor.*\Z=Admin
\A/propertiespanel.*\Z=Admin
\A/subscriptionadmin.*\Z=Admin
\A/resetrepository.*\Z=Admin
\A/viewaction.*solution.admin.*\Z=Admin
\A/scheduleradmin.*\Z=Admin
\A/publish.*\Z=Admin
\A/logout.*\Z=Anonymous
\A/.*\Z=Authenticated
        ]]>
      </value>
    </property>
  </bean>
</beans>

Troubleshooting

Importing the client certificate into Firefox on Ubuntu

For Firefox on Ubuntu, there is a known issue. A workaround shows how to install this client certificate using a command-line tool. Make sure Firefox is closed during execution of this command.

# install libnss3-tools package if necessary before running pk12util
pk12util -i me.pkcs12 -d ~/.mozilla/firefox/xxxxxxxx.default

Related Info