package com.eucalyptus.auth.login; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Map; import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.log4j.Logger; import org.apache.xml.security.utils.Base64; import com.eucalyptus.auth.Groups; import com.eucalyptus.auth.Users; import com.eucalyptus.auth.api.BaseLoginModule; import com.eucalyptus.auth.crypto.Hmac; import com.eucalyptus.auth.principal.User; public class Hmacv1LoginModule extends BaseLoginModule<HmacCredentials> { private static Logger LOG = Logger.getLogger( Hmacv1LoginModule.class ); public Hmacv1LoginModule() {} @Override public boolean accepts( ) { return super.getCallbackHandler( ) instanceof HmacCredentials && ((HmacCredentials)super.getCallbackHandler( )).getSignatureVersion( ).equals( 1 ); } @Override public boolean authenticate( HmacCredentials credentials ) throws Exception { String sig = credentials.getSignature( ); SecurityContext.enqueueSignature( sig ); User user = Users.lookupQueryId( credentials.getQueryId( ) ); String secretKey = user.getSecretKey( ); String canonicalString = this.makeSubjectString( credentials.getParameters( ) ); String computedSig = this.getSignature( secretKey, canonicalString, credentials.getSignatureMethod( ) ); String decodedSig = URLDecoder.decode( sig ).replaceAll( "=", "" ); if ( !computedSig.equals( sig.replaceAll( "=", "" ) ) && !computedSig.equals( decodedSig ) && !computedSig.equals( sig ) ) { return false; } super.setCredential( credentials.getQueryId( ) ); super.setPrincipal( user ); super.getGroups( ).addAll( Groups.lookupUserGroups( super.getPrincipal( ) ) ); return true; } @Override public void reset( ) {} private String makeSubjectString( final Map<String, String> parameters ) throws UnsupportedEncodingException { String paramString = ""; Set<String> sortedKeys = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER ); sortedKeys.addAll( parameters.keySet( ) ); for ( String key : sortedKeys ) paramString = paramString.concat( key ).concat( parameters.get( key ).replaceAll( "\\+", " " ) ); try { return new String(URLCodec.decodeUrl( paramString.getBytes() ) ); } catch ( DecoderException e ) { return paramString; } } public String getSignature( final String queryKey, final String subject, final Hmac mac ) throws AuthenticationException { SecretKeySpec signingKey = new SecretKeySpec( queryKey.getBytes( ), mac.toString( ) ); try { Mac digest = mac.getInstance( ); digest.init( signingKey ); byte[] rawHmac = digest.doFinal( subject.getBytes( ) ); return Base64.encode( rawHmac ).replaceAll( "=", "" ); } catch ( Exception e ) { LOG.error( e, e ); throw new AuthenticationException( "Failed to compute signature" ); } } }