/*
* Copyright 2012 Research Studios Austria Forschungsges.m.b.H.
*
* Licensed 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 won.cryptography.activemq;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ConsumerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import won.cryptography.ssl.AliasFromFingerprintGenerator;
import won.cryptography.ssl.AliasGenerator;
import java.security.cert.X509Certificate;
/**
* BrokerFilter implementation that authorizes consumers if their TLS certificate digest matches
* the suffix of the queue name they want to listen to.
*/
public class CertificateCheckingBrokerFilter extends BrokerFilter
{
private String queueNamePrefixToCheck;
private AliasGenerator aliasGenerator = new AliasFromFingerprintGenerator();
private final Logger logger = LoggerFactory.getLogger(getClass());
public CertificateCheckingBrokerFilter(final Broker next, String queueNamePrefixToCheck) {
super(next);
this.queueNamePrefixToCheck = queueNamePrefixToCheck;
}
@Override
public Subscription addConsumer(final ConnectionContext context, final ConsumerInfo info) throws Exception {
assert info != null : "ConsumerInfo must not be null";
assert context != null : "ConnectionContext must not be null";
if (shouldCheck(info)){
boolean checkPassed = false;
try {
checkPassed = isOwnerAllowedToConsume(context, info);
} catch (Exception e){
throw new SecurityException("could not perform access control check for consumer " + info.getConsumerId()
+" and destination "+ info.getDestination());
}
if (!checkPassed) throw new SecurityException("consumer " + info.getConsumerId()
+" not allowed to consume from destination "+ info.getDestination());
}
logger.debug("consumer added. destination: {}, consumerId: {}", info.getDestination(), info.getConsumerId());
return super.addConsumer(context, info);
}
/**
* Owner id is defined as a sha3-224 digest of the owner's certificate , based on the results of
* comparing the owner id in queue name and the provided certificate fingerprint, the access
* to read from that queue can be granted or denied.
* @param context
* @param info
* @return
*/
private boolean isOwnerAllowedToConsume(final ConnectionContext context, final ConsumerInfo info) {
logger.debug("checking if consumer {} is allowed to consume {} ", info.getConsumerId(), info.getDestination());
if (context.getConnectionState().getInfo().getTransportContext() instanceof X509Certificate[]) {
X509Certificate ownerCert = ((X509Certificate[]) context.getConnectionState().getInfo().getTransportContext())[0];
String certificateDigest = null;
try {
certificateDigest = aliasGenerator.generateAlias(ownerCert);
logger.debug("digest value of certificate: {}", certificateDigest);
} catch (Exception e) {
new IllegalArgumentException("Could not calculate sha-1 of owner certificate", e);
}
String forOwnerId = info.getDestination().getPhysicalName().substring(queueNamePrefixToCheck.length());
logger.debug("owner id suffix of queue name: {}", forOwnerId);
if (certificateDigest.equals(forOwnerId)) {
logger.debug("allowing to consume");
return true;
}
logger.info("denying message consumption to owner as public key hash does not equal owner id");
return false;
} else {
logger.info("denying message consumption to owner transportContext is not an X.509 certificate");
return false;
}
}
private boolean shouldCheck(final ConsumerInfo info) {
if (info.getDestination().getPhysicalName().indexOf(queueNamePrefixToCheck) == 0) {
return true;
}
return false;
}
}