/*
* Copyright 2007 Jesse Peterson
*
* 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 com.jpeterson.littles3.bo;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Base64;
import com.jpeterson.littles3.S3ObjectRequest;
/**
* Performs Amazon S3 Authentication.
*
* @author Jesse Peterson
*/
public class S3Authenticator implements Authenticator {
private static final String HEADER_AUTHORIZATION = "Authorization";
private static final String AUTHORIZATION_TYPE = "AWS";
private UserDirectory userDirectory;
/**
* Empty constructor.
*/
public S3Authenticator() {
}
/**
* Authenticate the request using the prescribed Amazon S3 authentication
* mechanisms.
*
* @param req
* The original HTTP request.
* @param s3Request
* The S3 specific information for authenticating the request.
* @return The authenticated <code>CanonicalUser</code> making the request.
* @throws RequestTimeTooSkewedException
* Thrown if the request timestamp is outside of the allotted
* timeframe.
*/
public CanonicalUser authenticate(HttpServletRequest req,
S3ObjectRequest s3Request) throws AuthenticatorException {
// check to see if anonymous request
String authorization = req.getHeader(HEADER_AUTHORIZATION);
if (authorization == null) {
return new CanonicalUser(CanonicalUser.ID_ANONYMOUS);
}
// attempting to be authenticated request
if (false) {
// check timestamp of request
Date timestamp = s3Request.getTimestamp();
if (timestamp == null) {
throw new RequestTimeTooSkewedException("No timestamp provided");
}
GregorianCalendar calendar = new GregorianCalendar();
Date now = calendar.getTime();
calendar.add(Calendar.MINUTE, 15);
Date maximumDate = calendar.getTime();
calendar.add(Calendar.MINUTE, -30);
Date minimumDate = calendar.getTime();
if (timestamp.before(minimumDate)) {
throw new RequestTimeTooSkewedException("Timestamp ["
+ timestamp + "] too old. System time: " + now);
}
if (timestamp.after(maximumDate)) {
throw new RequestTimeTooSkewedException("Timestamp ["
+ timestamp + "] too new. System time: " + now);
}
}
// authenticate request
String[] fields = authorization.split(" ");
if (fields.length != 2) {
throw new InvalidSecurityException(
"Unsupported authorization format");
}
if (!fields[0].equals(AUTHORIZATION_TYPE)) {
throw new InvalidSecurityException(
"Unsupported authorization type: " + fields[0]);
}
String[] keys = fields[1].split(":");
if (keys.length != 2) {
throw new InvalidSecurityException(
"Invalid AWSAccesskeyId:Signature");
}
String accessKeyId = keys[0];
String signature = keys[1];
String secretAccessKey = userDirectory
.getAwsSecretAccessKey(accessKeyId);
String calculatedSignature;
try {
SecretKey key = new SecretKeySpec(secretAccessKey.getBytes(),
"HmacSHA1");
Mac m = Mac.getInstance("HmacSHA1");
m.init(key);
m.update(s3Request.getStringToSign().getBytes());
byte[] mac = m.doFinal();
calculatedSignature = new String(Base64.encodeBase64(mac));
} catch (NoSuchAlgorithmException e) {
throw new InvalidSecurityException(e);
} catch (InvalidKeyException e) {
throw new InvalidSecurityException(e);
}
System.out.println("-----------------");
System.out.println("signature: " + signature);
System.out.println("calculatedSignature: " + calculatedSignature);
System.out.println("-----------------");
if (calculatedSignature.equals(signature)) {
// authenticated!
return userDirectory.getCanonicalUser(secretAccessKey);
} else {
throw new SignatureDoesNotMatchException(
"Provided signature doesn't match calculated value");
}
}
/**
* Get the <code>UserDirectory</code> for accessing user information for
* authentication.
*
* @return The <code>UserDirectory</code> for accessing user information for
* authentication.
*/
public UserDirectory getUserDirectory() {
return userDirectory;
}
/**
* Set the <code>UserDirectory</code> for accessing user information for
* authentication.
*
* @param userDirectory
* The <code>UserDirectory</code> for accessing user information
* for authentication.
*/
public void setUserDirectory(UserDirectory userDirectory) {
this.userDirectory = userDirectory;
}
}