//
// typica - A client library for Amazon Web Services
// Copyright (C) 2007 Xerox Corporation
//
// 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.xerox.amazonws.common;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* This class provides common code to the query and rest connection classes
*
* @author D. Kavanagh
* @author developer@dotech.com
*/
public abstract class AWSConnection {
private String awsAccessId;
private String awsSecretKey;
private boolean isSecure;
private String server;
private int port;
protected Map <String, List<String>> headers;
// used for caching last used Mac obj.. to save time 99.99% of the time
private static Mac mac;
private static String lastSecretKey;
/**
* Initializes the queue service with your AWS login information.
*
* @param awsAccessId The your user key into AWS
* @param awsSecretKey The secret string used to generate signatures for authentication.
* @param isSecure True if the data should be encrypted on the wire on the way to or from SQS.
* @param server Which host to connect to. Usually, this will be s3.amazonaws.com
* @param port Which port to use.
*/
public AWSConnection(String awsAccessId, String awsSecretKey, boolean isSecure,
String server, int port)
{
this.awsAccessId = awsAccessId;
this.awsSecretKey = awsSecretKey;
this.isSecure = isSecure;
this.server = server;
this.port = port;
this.headers = new TreeMap<String, List<String>>();
}
/**
* This method provides the URL for the queue service based on initialization.
*
* @return generated queue service url
*/
public URL getUrl() {
try {
return makeURL("");
} catch (MalformedURLException ex) {
return null;
}
}
protected String getAwsAccessKeyId() {
return this.awsAccessId;
}
protected String getSecretAccessKey() {
return this.awsSecretKey;
}
protected boolean isSecure() {
return this.isSecure;
}
protected String getServer() {
return this.server;
}
protected int getPort() {
return this.port;
}
/**
* Create a new URL object for a given resource.
* @param resource The resource name (bucketName + "/" + key).
*/
protected URL makeURL(String resource) throws MalformedURLException {
String protocol = this.isSecure ? "https" : "http";
return new URL(protocol, this.server, this.port, "/"+resource);
}
/**
* Calculate the HMAC/SHA1 on a string.
* @param awsSecretKey passcode to sign it with
* @param canonicalString data to sign
* @return signature
* @throws NoSuchAlgorithmException If the algorithm does not exist. Unlikely
* @throws InvalidKeyException If the key is invalid.
*/
protected String encode(String awsSecretKey, String canonicalString,
boolean urlencode)
{
// The following HMAC/SHA1 code for the signature is taken from the
// AWS Platform's implementation of RFC2104 (amazon.webservices.common.Signature)
//
// Acquire an HMAC/SHA1 from the raw key bytes.
SecretKeySpec signingKey =
new SecretKeySpec(awsSecretKey.getBytes(), "HmacSHA1");
// Acquire the MAC instance and initialize with the signing key.
if (mac == null || !lastSecretKey.equals(awsSecretKey)) {
try {
mac = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException e) {
// should not happen
throw new RuntimeException("Could not find sha1 algorithm", e);
}
try {
mac.init(signingKey);
} catch (InvalidKeyException e) {
// also should not happen
mac = null;
throw new RuntimeException("Could not initialize the MAC algorithm", e);
}
lastSecretKey = awsSecretKey;
}
// Compute the HMAC on the digest, and set it.
byte [] signedBytes = null;
synchronized (mac) {
signedBytes = mac.doFinal(canonicalString.getBytes());
}
String b64 = new String(Base64.encodeBase64(signedBytes));
if (urlencode) {
return urlencode(b64);
} else {
return b64;
}
}
protected String urlencode(String unencoded) {
try {
return URLEncoder.encode(unencoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
// should never happen
throw new RuntimeException("Could not url encode to UTF-8", e);
}
}
}