| /* |
| * 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.geode.security.templates; |
| |
| import java.io.FileInputStream; |
| import java.security.KeyStore; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.Principal; |
| import java.security.Signature; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509Certificate; |
| import java.security.spec.InvalidKeySpecException; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.LogWriter; |
| import org.apache.geode.distributed.DistributedMember; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| import org.apache.geode.security.AuthenticationFailedException; |
| import org.apache.geode.security.Authenticator; |
| |
| /** |
| * An implementation of {@link Authenticator} that uses PKCS. |
| */ |
| public class PKCSAuthenticator implements Authenticator { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| public static final String PUBLIC_KEY_FILE = "security-publickey-filepath"; |
| public static final String PUBLIC_KEYSTORE_PASSWORD = "security-publickey-pass"; |
| |
| private String pubKeyFilePath; |
| private String pubKeyPass; |
| private Map aliasCertificateMap; |
| |
| private LogWriter systemLogWriter; |
| private LogWriter securityLogWriter; |
| |
| public static Authenticator create() { |
| return new PKCSAuthenticator(); |
| } |
| |
| @Override |
| public void init(final Properties securityProperties, final LogWriter systemLogWriter, |
| final LogWriter securityLogWriter) throws AuthenticationFailedException { |
| this.systemLogWriter = systemLogWriter; |
| this.securityLogWriter = securityLogWriter; |
| |
| this.pubKeyFilePath = securityProperties.getProperty(PUBLIC_KEY_FILE); |
| if (this.pubKeyFilePath == null) { |
| throw new AuthenticationFailedException("PKCSAuthenticator: property " + PUBLIC_KEY_FILE |
| + " not specified as the public key file."); |
| } |
| |
| this.pubKeyPass = securityProperties.getProperty(PUBLIC_KEYSTORE_PASSWORD); |
| this.aliasCertificateMap = new HashMap(); |
| |
| populateMap(); |
| } |
| |
| @Override |
| public Principal authenticate(final Properties credentials, final DistributedMember member) |
| throws AuthenticationFailedException { |
| final String alias = (String) credentials.get(PKCSAuthInit.KEYSTORE_ALIAS); |
| if (alias == null || alias.length() <= 0) { |
| throw new AuthenticationFailedException("No alias received"); |
| } |
| |
| try { |
| final X509Certificate cert = getCertificate(alias); |
| if (cert == null) { |
| throw newException("No certificate found for alias:" + alias); |
| } |
| |
| final byte[] signatureBytes = (byte[]) credentials.get(PKCSAuthInit.SIGNATURE_DATA); |
| if (signatureBytes == null) { |
| throw newException( |
| "signature data property [" + PKCSAuthInit.SIGNATURE_DATA + "] not provided"); |
| } |
| |
| final Signature sig = Signature.getInstance(cert.getSigAlgName()); |
| sig.initVerify(cert); |
| sig.update(alias.getBytes("UTF-8")); |
| |
| if (!sig.verify(signatureBytes)) { |
| throw newException("verification of client signature failed"); |
| } |
| |
| return new PKCSPrincipal(alias); |
| |
| } catch (Exception ex) { |
| throw newException(ex.toString(), ex); |
| } |
| } |
| |
| @Override |
| public void close() {} |
| |
| private void populateMap() { |
| try { |
| final KeyStore keyStore = KeyStore.getInstance("JKS"); |
| final char[] passPhrase = this.pubKeyPass != null ? this.pubKeyPass.toCharArray() : null; |
| final FileInputStream keyStoreFile = new FileInputStream(this.pubKeyFilePath); |
| |
| try { |
| keyStore.load(keyStoreFile, passPhrase); |
| } finally { |
| keyStoreFile.close(); |
| } |
| |
| for (Enumeration e = keyStore.aliases(); e.hasMoreElements();) { |
| final Object alias = e.nextElement(); |
| final Certificate cert = keyStore.getCertificate((String) alias); |
| if (cert instanceof X509Certificate) { |
| this.aliasCertificateMap.put(alias, cert); |
| } |
| } |
| |
| } catch (Exception e) { |
| throw new AuthenticationFailedException( |
| "Exception while getting public keys: " + e.getMessage(), e); |
| } |
| } |
| |
| private AuthenticationFailedException newException(final String message, final Exception cause) { |
| final String fullMessage = |
| "PKCSAuthenticator: Authentication of client failed due to: " + message; |
| if (cause != null) { |
| return new AuthenticationFailedException(fullMessage, cause); |
| } else { |
| return new AuthenticationFailedException(fullMessage); |
| } |
| } |
| |
| private AuthenticationFailedException newException(final String message) { |
| return newException(message, null); |
| } |
| |
| private X509Certificate getCertificate(final String alias) |
| throws NoSuchAlgorithmException, InvalidKeySpecException { |
| if (this.aliasCertificateMap.containsKey(alias)) { |
| return (X509Certificate) this.aliasCertificateMap.get(alias); |
| } |
| return null; |
| } |
| } |