From 1eb4619faa3d4915e56e4af273eae53ebb97d663 Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Sun, 13 Dec 2009 05:16:26 +0000 Subject: [PATCH] Issue 131: extract out bouncycastle git-svn-id: http://jclouds.googlecode.com/svn/trunk@2408 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../__clientName__RestClientModule.java | 6 +- .../java/__clientName__AsyncClientTest.java | 4 +- ...mosObjectToEntityAndMetadataToHeaders.java | 9 +- .../saas/blobstore/AtmosAsyncBlobStore.java | 9 +- .../saas/blobstore/AtmosBlobStore.java | 8 +- .../functions/ObjectToBlobMetadata.java | 8 +- .../atmosonline/saas/filters/SignRequest.java | 20 +- .../saas/AtmosStorageClientTest.java | 4 +- .../jclouds/aws/ec2/filters/FormSigner.java | 9 +- .../s3/filters/RequestAuthorizeSignature.java | 8 +- .../ParseObjectMetadataFromHeaders.java | 8 +- .../jclouds/aws/s3/xml/ListBucketHandler.java | 9 +- .../ParseObjectMetadataFromHeadersTest.java | 10 +- .../aws/s3/xml/ListBucketHandlerTest.java | 24 +- .../blob/binders/BindAzureBlobToEntity.java | 6 +- .../filters/SharedKeyAuthentication.java | 9 +- .../storage/blob/AzureBlobClientLiveTest.java | 20 +- .../storage/blob/AzureBlobClientTest.java | 4 +- .../config/RestAzureBlobClientModuleTest.java | 4 +- .../filters/SharedKeyAuthenticationTest.java | 4 +- .../storage/queue/AzureQueueClientTest.java | 4 +- .../blobstore/binders/BindBlobToEntity.java | 15 +- ...ityAndUserMetadataToHeadersWithPrefix.java | 9 +- .../blobstore/functions/GenerateMD5.java | 4 +- .../functions/GenerateMD5Result.java | 4 +- ...ParseSystemAndUserMetadataFromHeaders.java | 9 +- ...leGenerateMD5.java => JCEGenerateMD5.java} | 8 +- ...5Result.java => JCEGenerateMD5Result.java} | 37 +- .../ParseBlobMetadataFromHeadersTest.java | 3 +- .../internal/BaseBlobIntegrationTest.java | 6 +- .../internal/BaseBlobLiveTest.java | 4 +- .../internal/StubAsyncBlobStore.java | 14 +- core/pom.xml | 5 - .../main/java/org/jclouds/http/HttpUtils.java | 209 +-- .../http/filters/BasicAuthentication.java | 12 +- .../functions/ParseContentMD5FromHeaders.java | 7 +- .../org/jclouds/util/EncryptionService.java | 89 + .../org/jclouds/util/internal/Base64.java | 1459 +++++++++++++++++ .../util/internal/BaseEncryptionService.java | 85 + .../util/internal/JCEEncryptionService.java | 125 ++ .../internal/SimpleDateFormatDateService.java | 172 ++ .../http/filters/BasicAuthenticationTest.java | 3 +- .../jclouds/http/internal/WireLiveTest.java | 20 +- .../jclouds/util/EncryptionServiceTest.java | 148 ++ .../java/org/jclouds/util/HttpUtilsTest.java | 98 -- extensions/bouncycastle/pom.xml | 46 + .../BouncyCastleEncryptionService.java | 120 ++ .../util/internal/EncryptionService.java | 90 + .../BouncyCastleEncryptionServiceTest.java | 23 + extensions/pom.xml | 1 + .../pcs2/config/PCSRestClientModule.java | 7 +- .../org/jclouds/mezeo/pcs2/PCSClientTest.java | 5 +- .../jclouds/mezeo/pcs2/PCSCloudLiveTest.java | 5 +- .../org/jclouds/mezeo/pcs2/PCSCloudTest.java | 8 +- .../nirvanix/sdn/SDNClientLiveTest.java | 6 +- .../binders/BindCFObjectToEntity.java | 11 +- .../functions/ObjectToBlobMetadata.java | 9 +- .../functions/ResourceToObjectInfo.java | 23 +- .../functions/ParseObjectInfoFromHeaders.java | 8 +- .../ParseObjectInfoListFromJsonResponse.java | 8 +- .../options/CreateServerOptions.java | 4 +- .../cloudfiles/CloudFilesClientLiveTest.java | 28 +- .../miro/RimuHostingAsyncClientTest.java | 57 +- .../config/TwitterRestClientModule.java | 7 +- .../jclouds/twitter/TwitterClientTest.java | 5 +- .../VCloudDiscoveryRestClientModule.java | 6 +- .../org/jclouds/vcloud/VCloudLoginTest.java | 7 +- 67 files changed, 2690 insertions(+), 526 deletions(-) rename blobstore/src/main/java/org/jclouds/blobstore/functions/impl/{BouncyCastleGenerateMD5.java => JCEGenerateMD5.java} (81%) rename blobstore/src/main/java/org/jclouds/blobstore/functions/impl/{BouncyCastleGenerateMD5Result.java => JCEGenerateMD5Result.java} (54%) create mode 100644 core/src/main/java/org/jclouds/util/EncryptionService.java create mode 100644 core/src/main/java/org/jclouds/util/internal/Base64.java create mode 100755 core/src/main/java/org/jclouds/util/internal/BaseEncryptionService.java create mode 100644 core/src/main/java/org/jclouds/util/internal/JCEEncryptionService.java create mode 100755 core/src/main/java/org/jclouds/util/internal/SimpleDateFormatDateService.java create mode 100644 core/src/test/java/org/jclouds/util/EncryptionServiceTest.java create mode 100644 extensions/bouncycastle/pom.xml create mode 100644 extensions/bouncycastle/src/main/java/org/jclouds/util/internal/BouncyCastleEncryptionService.java create mode 100644 extensions/bouncycastle/src/main/java/org/jclouds/util/internal/EncryptionService.java create mode 100644 extensions/bouncycastle/src/test/java/org/jclouds/util/internal/BouncyCastleEncryptionServiceTest.java diff --git a/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/main/java/config/__clientName__RestClientModule.java b/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/main/java/config/__clientName__RestClientModule.java index 82ce19ebfe..4dac7744d0 100644 --- a/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/main/java/config/__clientName__RestClientModule.java +++ b/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/main/java/config/__clientName__RestClientModule.java @@ -35,6 +35,7 @@ import org.jclouds.http.RequiresHttp; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestClientFactory; +import org.jclouds.util.EncryptionService; import ${package}.${clientName}; import ${package}.${clientName}Client; @@ -63,9 +64,10 @@ public class ${clientName}RestClientModule extends AbstractModule { @Singleton public BasicAuthentication provideBasicAuthentication( @Named(${clientName}Constants.PROPERTY_${ucaseClientName}_USER) String user, - @Named(${clientName}Constants.PROPERTY_${ucaseClientName}_PASSWORD) String password) + @Named(${clientName}Constants.PROPERTY_${ucaseClientName}_PASSWORD) String password, + EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication(user, password); + return new BasicAuthentication(user, password, encryptionService); } @Provides diff --git a/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/test/java/__clientName__AsyncClientTest.java b/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/test/java/__clientName__AsyncClientTest.java index 69ef87a926..2baf71a702 100644 --- a/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/test/java/__clientName__AsyncClientTest.java +++ b/archetypes/json-client-archetype/src/main/resources/archetype-resources/src/test/java/__clientName__AsyncClientTest.java @@ -102,9 +102,9 @@ public class ${clientName}AsyncClientTest extends RestClientTest<${clientName}As @SuppressWarnings("unused") @Provides @Singleton - public BasicAuthentication provideBasicAuthentication() + public BasicAuthentication provideBasicAuthentication(EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication("foo", "bar"); + return new BasicAuthentication("foo", "bar", encryptionService); } }; diff --git a/atmos/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java b/atmos/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java index 380df7d859..148b8980da 100644 --- a/atmos/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java +++ b/atmos/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java @@ -30,15 +30,18 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.atmosonline.saas.domain.AtmosObject; import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpUtils; import org.jclouds.rest.Binder; +import org.jclouds.util.EncryptionService; public class BindAtmosObjectToEntityAndMetadataToHeaders implements Binder { private final BindUserMetadataToHeaders metaBinder; + private final EncryptionService encryptionService; @Inject - protected BindAtmosObjectToEntityAndMetadataToHeaders(BindUserMetadataToHeaders metaBinder) { + protected BindAtmosObjectToEntityAndMetadataToHeaders(BindUserMetadataToHeaders metaBinder, + EncryptionService encryptionService) { this.metaBinder = metaBinder; + this.encryptionService = encryptionService; } public void bindToRequest(HttpRequest request, Object entity) { @@ -55,7 +58,7 @@ public class BindAtmosObjectToEntityAndMetadataToHeaders implements Binder { if (object.getContentMetadata().getContentMD5() != null) { request.getHeaders().put("Content-MD5", - HttpUtils.toBase64String(object.getContentMetadata().getContentMD5())); + encryptionService.toBase64String(object.getContentMetadata().getContentMD5())); } metaBinder.bindToRequest(request, object.getUserMetadata()); } diff --git a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosAsyncBlobStore.java b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosAsyncBlobStore.java index 72d4d4ff34..426bbf5555 100644 --- a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosAsyncBlobStore.java +++ b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosAsyncBlobStore.java @@ -52,15 +52,16 @@ import org.jclouds.blobstore.domain.Blob.Factory; import org.jclouds.blobstore.functions.BlobToHttpGetOptions; import org.jclouds.blobstore.strategy.ClearListStrategy; import org.jclouds.concurrent.FutureFunctionCallable; -import org.jclouds.http.HttpUtils; import org.jclouds.http.options.GetOptions; import org.jclouds.logging.Logger.LoggerFactory; +import org.jclouds.util.EncryptionService; import org.jclouds.util.Utils; import com.google.common.base.Function; import com.google.common.base.Supplier; public class AtmosAsyncBlobStore extends BaseAtmosBlobStore implements AsyncBlobStore { + private final EncryptionService encryptionService; @Inject public AtmosAsyncBlobStore(AtmosStorageAsyncClient async, AtmosStorageClient sync, @@ -69,10 +70,12 @@ public class AtmosAsyncBlobStore extends BaseAtmosBlobStore implements AsyncBlob ObjectToBlob object2Blob, BlobToObject blob2Object, BlobStoreListOptionsToListOptions container2ContainerListOptions, BlobToHttpGetOptions blob2ObjectGetOptions, - DirectoryEntryListToResourceMetadataList container2ResourceList, ExecutorService service) { + DirectoryEntryListToResourceMetadataList container2ResourceList, + ExecutorService service, EncryptionService encryptionService) { super(async, sync, blobFactory, logFactory, clearContainerStrategy, object2BlobMd, object2Blob, blob2Object, container2ContainerListOptions, blob2ObjectGetOptions, container2ResourceList, service); + this.encryptionService = encryptionService; } /** @@ -194,7 +197,7 @@ public class AtmosAsyncBlobStore extends BaseAtmosBlobStore implements AsyncBlob } if (blob.getMetadata().getContentMD5() != null) blob.getMetadata().getUserMetadata().put("content-md5", - HttpUtils.toHexString(blob.getMetadata().getContentMD5())); + encryptionService.toHexString(blob.getMetadata().getContentMD5())); sync.createFile(container, blob2Object.apply(blob)); return path; } catch (InterruptedException e) { diff --git a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosBlobStore.java b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosBlobStore.java index 71151c1d7f..c69572375a 100644 --- a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosBlobStore.java +++ b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/AtmosBlobStore.java @@ -47,14 +47,15 @@ import org.jclouds.blobstore.domain.ResourceMetadata; import org.jclouds.blobstore.domain.Blob.Factory; import org.jclouds.blobstore.functions.BlobToHttpGetOptions; import org.jclouds.blobstore.strategy.ClearListStrategy; -import org.jclouds.http.HttpUtils; import org.jclouds.http.options.GetOptions; import org.jclouds.logging.Logger.LoggerFactory; +import org.jclouds.util.EncryptionService; import org.jclouds.util.Utils; import com.google.common.base.Supplier; public class AtmosBlobStore extends BaseAtmosBlobStore implements BlobStore { + private final EncryptionService encryptionService; @Inject public AtmosBlobStore(AtmosStorageAsyncClient async, AtmosStorageClient sync, @@ -63,10 +64,11 @@ public class AtmosBlobStore extends BaseAtmosBlobStore implements BlobStore { ObjectToBlob object2Blob, BlobToObject blob2Object, BlobStoreListOptionsToListOptions container2ContainerListOptions, BlobToHttpGetOptions blob2ObjectGetOptions, - DirectoryEntryListToResourceMetadataList container2ResourceList, ExecutorService service) { + DirectoryEntryListToResourceMetadataList container2ResourceList, ExecutorService service,EncryptionService encryptionService) { super(async, sync, blobFactory, logFactory, clearContainerStrategy, object2BlobMd, object2Blob, blob2Object, container2ContainerListOptions, blob2ObjectGetOptions, container2ResourceList, service); + this.encryptionService = encryptionService; } /** @@ -149,7 +151,7 @@ public class AtmosBlobStore extends BaseAtmosBlobStore implements BlobStore { deleteAndEnsurePathGone(path); if (blob.getMetadata().getContentMD5() != null) blob.getMetadata().getUserMetadata().put("content-md5", - HttpUtils.toHexString(blob.getMetadata().getContentMD5())); + encryptionService.toHexString(blob.getMetadata().getContentMD5())); sync.createFile(container, blob2Object.apply(blob)); return path; } diff --git a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java index 4f16264a7b..6b5c5abb04 100644 --- a/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java +++ b/atmos/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java @@ -35,7 +35,7 @@ import org.jclouds.atmosonline.saas.functions.AtmosObjectName; import org.jclouds.blobstore.domain.MutableBlobMetadata; import org.jclouds.blobstore.domain.ResourceType; import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl; -import org.jclouds.http.HttpUtils; +import org.jclouds.util.EncryptionService; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; @@ -50,10 +50,12 @@ public class ObjectToBlobMetadata implements Function systemMetadata = ImmutableSet.of("atime", "mtime", "ctime", "itime", "type", "uid", "gid", "objectid", "objname", "size", "nlink", "policyname", "content-md5"); + private final EncryptionService encryptionService; @Inject - protected ObjectToBlobMetadata(AtmosObjectName objectName) { + protected ObjectToBlobMetadata(AtmosObjectName objectName, EncryptionService encryptionService) { this.objectName = objectName; + this.encryptionService = encryptionService; } public MutableBlobMetadata apply(AtmosObject from) { @@ -62,7 +64,7 @@ public class ObjectToBlobMetadata implements Function timeStampProvider; + private final EncryptionService encryptionService; + @Resource @Named(HttpConstants.SIGNATURE_LOGGER) Logger signatureLog = Logger.NULL; - + @Inject public SignRequest(SignatureWire signatureWire, @Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String uid, @Named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY) String encodedKey, - @TimeStamp Provider timeStampProvider) { + @TimeStamp Provider timeStampProvider, EncryptionService encryptionService) { this.signatureWire = signatureWire; this.uid = uid; - this.key = HttpUtils.fromBase64String(encodedKey); + this.key = encryptionService.fromBase64String(encodedKey); this.timeStampProvider = timeStampProvider; + this.encryptionService = encryptionService; } public void filter(HttpRequest request) throws HttpException { @@ -110,7 +114,7 @@ public class SignRequest implements HttpRequestFilter { public String signString(String toSign) { String signature; try { - signature = HttpUtils.hmacSha1Base64(toSign, key); + signature = encryptionService.hmacSha1Base64(toSign, key); } catch (Exception e) { throw new HttpException("error signing request", e); } @@ -139,12 +143,14 @@ public class SignRequest implements HttpRequestFilter { if (header.startsWith("x-emc-")) { // Convert all header names to lowercase. toSign.append(header.toLowerCase()).append(":"); - // For headers with values that span multiple lines, convert them into one line by replacing any + // For headers with values that span multiple lines, convert them into one line by + // replacing any // newline characters and extra embedded white spaces in the value. for (String value : request.getHeaders().get(header)) toSign.append(value.replaceAll("\r?\n", "").replaceAll(" ", " ")).append(" "); toSign.deleteCharAt(toSign.lastIndexOf(" ")); - // Concatenate all headers together, using newlines (\n) separating each header from the next one. + // Concatenate all headers together, using newlines (\n) separating each header from the + // next one. toSign.append("\n"); } } @@ -155,7 +161,7 @@ public class SignRequest implements HttpRequestFilter { @VisibleForTesting void appendHttpHeaders(HttpRequest request, StringBuilder toSign) { - // Only the value is used, not the header + // Only the value is used, not the header // name. If a request does not include the header, this is an empty string. for (String header : new String[] { HttpHeaders.CONTENT_TYPE, "Range" }) toSign.append(valueOrEmpty(request.getHeaders().get(header)).toLowerCase()).append("\n"); diff --git a/atmos/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java b/atmos/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java index 2f92253008..4754295f8e 100644 --- a/atmos/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java +++ b/atmos/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java @@ -46,7 +46,6 @@ import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest; import org.jclouds.blobstore.config.BlobStoreObjectModule; import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; -import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ReturnVoidIf2xx; import org.jclouds.http.options.GetOptions; @@ -57,6 +56,7 @@ import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Jsr330; import org.jclouds.util.TimeStamp; +import org.jclouds.util.internal.Base64; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -303,7 +303,7 @@ public class AtmosStorageClientTest extends RestClientTest dateService; + private final EncryptionService encryptionService; @Resource @Named(HttpConstants.SIGNATURE_LOGGER) - private final Logger signatureLog = Logger.NULL; + private Logger signatureLog = Logger.NULL; @Inject public FormSigner(SignatureWire signatureWire, @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String accessKey, @Named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey, - @TimeStamp Provider dateService) { + @TimeStamp Provider dateService, EncryptionService encryptionService) { this.signatureWire = signatureWire; this.accessKey = accessKey; this.secretKey = secretKey; this.dateService = dateService; + this.encryptionService = encryptionService; } public void filter(HttpRequest in) throws HttpException { @@ -157,7 +160,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner { public String signString(String stringToSign) { String signature; try { - signature = HttpUtils.hmacSha256Base64(stringToSign, secretKey.getBytes()); + signature = encryptionService.hmacSha256Base64(stringToSign, secretKey.getBytes()); if (signatureWire.enabled()) signatureWire.input(IOUtils.toInputStream(signature)); } catch (Exception e) { diff --git a/aws/core/src/main/java/org/jclouds/aws/s3/filters/RequestAuthorizeSignature.java b/aws/core/src/main/java/org/jclouds/aws/s3/filters/RequestAuthorizeSignature.java index 428a3a5d70..a719077497 100755 --- a/aws/core/src/main/java/org/jclouds/aws/s3/filters/RequestAuthorizeSignature.java +++ b/aws/core/src/main/java/org/jclouds/aws/s3/filters/RequestAuthorizeSignature.java @@ -47,6 +47,7 @@ import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpUtils; import org.jclouds.http.internal.SignatureWire; import org.jclouds.logging.Logger; +import org.jclouds.util.EncryptionService; import org.jclouds.util.TimeStamp; import com.google.common.annotations.VisibleForTesting; @@ -67,6 +68,8 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign private final String accessKey; private final String secretKey; private final Provider timeStampProvider; + private final EncryptionService encryptionService; + @Resource @Named(HttpConstants.SIGNATURE_LOGGER) Logger signatureLog = Logger.NULL; @@ -75,11 +78,12 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign public RequestAuthorizeSignature(SignatureWire signatureWire, @Named(S3Constants.PROPERTY_AWS_ACCESSKEYID) String accessKey, @Named(S3Constants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey, - @TimeStamp Provider timeStampProvider) { + @TimeStamp Provider timeStampProvider, EncryptionService encryptionService) { this.signatureWire = signatureWire; this.accessKey = accessKey; this.secretKey = secretKey; this.timeStampProvider = timeStampProvider; + this.encryptionService = encryptionService; } public void filter(HttpRequest request) throws HttpException { @@ -115,7 +119,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign public String signString(String toSign) { String signature; try { - signature = HttpUtils.hmacSha1Base64(toSign, secretKey.getBytes()); + signature = encryptionService.hmacSha1Base64(toSign, secretKey.getBytes()); } catch (Exception e) { throw new HttpException("error signing request", e); } diff --git a/aws/core/src/main/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeaders.java b/aws/core/src/main/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeaders.java index a626ccdcd3..5d1de2bbd8 100644 --- a/aws/core/src/main/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeaders.java +++ b/aws/core/src/main/java/org/jclouds/aws/s3/functions/ParseObjectMetadataFromHeaders.java @@ -32,9 +32,9 @@ import org.jclouds.aws.s3.reference.S3Headers; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders; import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpUtils; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.util.EncryptionService; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; @@ -50,12 +50,14 @@ public class ParseObjectMetadataFromHeaders implements Function, InvocationContext { private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser; private final BlobToObjectMetadata blobToObjectMetadata; + private final EncryptionService encryptionService; @Inject public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser, - BlobToObjectMetadata blobToObjectMetadata) { + BlobToObjectMetadata blobToObjectMetadata, EncryptionService encryptionService) { this.blobMetadataParser = blobMetadataParser; this.blobToObjectMetadata = blobToObjectMetadata; + this.encryptionService = encryptionService; } /** @@ -66,7 +68,7 @@ public class ParseObjectMetadataFromHeaders implements BlobMetadata base = blobMetadataParser.apply(from); MutableObjectMetadata to = blobToObjectMetadata.apply(base); addETagTo(from, to); - to.setContentMD5(HttpUtils.fromHexString(to.getETag().replaceAll("\"", ""))); + to.setContentMD5(encryptionService.fromHexString(to.getETag().replaceAll("\"", ""))); to.setCacheControl(from.getFirstHeaderOrNull(HttpHeaders.CACHE_CONTROL)); to.setContentDisposition(from.getFirstHeaderOrNull("Content-Disposition")); to.setContentEncoding(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_ENCODING)); diff --git a/aws/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java b/aws/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java index f5f5797305..2ba8805d46 100755 --- a/aws/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java +++ b/aws/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java @@ -34,9 +34,9 @@ import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass; import org.jclouds.aws.s3.domain.internal.BucketListObjectMetadata; import org.jclouds.aws.s3.domain.internal.TreeSetListBucketResponse; -import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseSax; import org.jclouds.util.DateService; +import org.jclouds.util.EncryptionService; import org.xml.sax.Attributes; import com.google.common.collect.Sets; @@ -58,6 +58,8 @@ public class ListBucketHandler extends ParseSax.HandlerWithResultadriancole.org.jclouds.aws.s3.amazons3testdelimiterapps/1000falseapps/02009-05-07T18:27:08.000Z"c82e6a0025c31c5de5947fda62ac51ab"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/12009-05-07T18:27:09.000Z"944fab2c5a9a6bacf07db5e688310d7a"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/22009-05-07T18:27:09.000Z"a227b8888045c8fd159fb495214000f0"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/32009-05-07T18:27:09.000Z"c9caa76c3dec53e2a192608ce73eef03"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/42009-05-07T18:27:09.000Z"1ce5d0dcc6154a647ea90c7bdf82a224"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/52009-05-07T18:27:09.000Z"79433524d87462ee05708a8ef894ed55"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/62009-05-07T18:27:10.000Z"dd00a060b28ddca8bc5a21a49e306f67"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/72009-05-07T18:27:10.000Z"8cd06eca6e819a927b07a285d750b100"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/82009-05-07T18:27:10.000Z"174495094d0633b92cbe46603eee6bad"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARDapps/92009-05-07T18:27:10.000Z"cd8a19b26fea8a827276df0ad11c580d"8e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0ferncamSTANDARD"; public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = " / apps/"; private DateService dateService; + private static final EncryptionService encryptionService = new JCEEncryptionService(); @BeforeTest @Override @@ -67,52 +69,52 @@ public class ListBucketHandlerTest extends BaseHandlerTest { "adriancole.org.jclouds.aws.s3.amazons3testdelimiter", ImmutableList.of( (ObjectMetadata) new BucketListObjectMetadata("apps/0", dateService .iso8601DateParse("2009-05-07T18:27:08.000Z"), - "\"c82e6a0025c31c5de5947fda62ac51ab\"", HttpUtils + "\"c82e6a0025c31c5de5947fda62ac51ab\"", encryptionService .fromHexString("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/1", dateService .iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"944fab2c5a9a6bacf07db5e688310d7a\"", HttpUtils + "\"944fab2c5a9a6bacf07db5e688310d7a\"", encryptionService .fromHexString("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/2", dateService .iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"a227b8888045c8fd159fb495214000f0\"", HttpUtils + "\"a227b8888045c8fd159fb495214000f0\"", encryptionService .fromHexString("a227b8888045c8fd159fb495214000f0"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/3", dateService .iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"c9caa76c3dec53e2a192608ce73eef03\"", HttpUtils + "\"c9caa76c3dec53e2a192608ce73eef03\"", encryptionService .fromHexString("c9caa76c3dec53e2a192608ce73eef03"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/4", dateService .iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"1ce5d0dcc6154a647ea90c7bdf82a224\"", HttpUtils + "\"1ce5d0dcc6154a647ea90c7bdf82a224\"", encryptionService .fromHexString("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/5", dateService .iso8601DateParse("2009-05-07T18:27:09.000Z"), - "\"79433524d87462ee05708a8ef894ed55\"", HttpUtils + "\"79433524d87462ee05708a8ef894ed55\"", encryptionService .fromHexString("79433524d87462ee05708a8ef894ed55"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/6", dateService .iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"dd00a060b28ddca8bc5a21a49e306f67\"", HttpUtils + "\"dd00a060b28ddca8bc5a21a49e306f67\"", encryptionService .fromHexString("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/7", dateService .iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"8cd06eca6e819a927b07a285d750b100\"", HttpUtils + "\"8cd06eca6e819a927b07a285d750b100\"", encryptionService .fromHexString("8cd06eca6e819a927b07a285d750b100"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/8", dateService .iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"174495094d0633b92cbe46603eee6bad\"", HttpUtils + "\"174495094d0633b92cbe46603eee6bad\"", encryptionService .fromHexString("174495094d0633b92cbe46603eee6bad"), 8, owner, StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/9", dateService .iso8601DateParse("2009-05-07T18:27:10.000Z"), - "\"cd8a19b26fea8a827276df0ad11c580d\"", HttpUtils + "\"cd8a19b26fea8a827276df0ad11c580d\"", encryptionService .fromHexString("cd8a19b26fea8a827276df0ad11c580d"), 8, owner, StorageClass.STANDARD)), "apps/", null, 1000, null, false, new TreeSet()); diff --git a/azure/src/main/java/org/jclouds/azure/storage/blob/binders/BindAzureBlobToEntity.java b/azure/src/main/java/org/jclouds/azure/storage/blob/binders/BindAzureBlobToEntity.java index 9ec116d2c4..2bdf373263 100644 --- a/azure/src/main/java/org/jclouds/azure/storage/blob/binders/BindAzureBlobToEntity.java +++ b/azure/src/main/java/org/jclouds/azure/storage/blob/binders/BindAzureBlobToEntity.java @@ -35,6 +35,7 @@ import org.jclouds.azure.storage.blob.domain.AzureBlob; import org.jclouds.azure.storage.blob.reference.AzureBlobConstants; import org.jclouds.blobstore.binders.BindBlobToEntityAndUserMetadataToHeadersWithPrefix; import org.jclouds.http.HttpRequest; +import org.jclouds.util.EncryptionService; public class BindAzureBlobToEntity extends BindBlobToEntityAndUserMetadataToHeadersWithPrefix { @@ -42,8 +43,9 @@ public class BindAzureBlobToEntity extends BindBlobToEntityAndUserMetadataToHead @Inject public BindAzureBlobToEntity(AzureBlobToBlob azureBlob2Blob, - @Named(AzureBlobConstants.PROPERTY_AZUREBLOB_METADATA_PREFIX) String prefix) { - super(prefix); + @Named(AzureBlobConstants.PROPERTY_AZUREBLOB_METADATA_PREFIX) String prefix, + EncryptionService encryptionService) { + super(prefix, encryptionService); this.azureBlob2Blob = azureBlob2Blob; } diff --git a/azure/src/main/java/org/jclouds/azure/storage/filters/SharedKeyAuthentication.java b/azure/src/main/java/org/jclouds/azure/storage/filters/SharedKeyAuthentication.java index bc989e6137..1079869f5e 100644 --- a/azure/src/main/java/org/jclouds/azure/storage/filters/SharedKeyAuthentication.java +++ b/azure/src/main/java/org/jclouds/azure/storage/filters/SharedKeyAuthentication.java @@ -44,6 +44,7 @@ import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpUtils; import org.jclouds.http.internal.SignatureWire; import org.jclouds.logging.Logger; +import org.jclouds.util.EncryptionService; import org.jclouds.util.TimeStamp; import com.google.common.annotations.VisibleForTesting; @@ -64,6 +65,7 @@ public class SharedKeyAuthentication implements HttpRequestFilter { private final String account; private final byte[] key; private final Provider timeStampProvider; + private final EncryptionService encryptionService; @Resource @Named(HttpConstants.SIGNATURE_LOGGER) Logger signatureLog = Logger.NULL; @@ -72,10 +74,11 @@ public class SharedKeyAuthentication implements HttpRequestFilter { public SharedKeyAuthentication(SignatureWire signatureWire, @Named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT) String account, @Named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY) String encodedKey, - @TimeStamp Provider timeStampProvider) { + @TimeStamp Provider timeStampProvider, EncryptionService encryptionService) { + this.encryptionService = encryptionService; this.signatureWire = signatureWire; this.account = account; - this.key = HttpUtils.fromBase64String(encodedKey); + this.key = encryptionService.fromBase64String(encodedKey); this.timeStampProvider = timeStampProvider; } @@ -111,7 +114,7 @@ public class SharedKeyAuthentication implements HttpRequestFilter { public String signString(String toSign) { String signature; try { - signature = HttpUtils.hmacSha256Base64(toSign, key); + signature = encryptionService.hmacSha256Base64(toSign, key); } catch (Exception e) { throw new HttpException("error signing request", e); } diff --git a/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientLiveTest.java b/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientLiveTest.java index 8bc7c6aefb..537df9e9ea 100644 --- a/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientLiveTest.java +++ b/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientLiveTest.java @@ -44,10 +44,11 @@ import org.jclouds.azure.storage.blob.options.CreateContainerOptions; import org.jclouds.azure.storage.domain.BoundedSortedSet; import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; import org.jclouds.http.options.GetOptions; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.util.EncryptionService; import org.jclouds.util.Utils; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; @@ -64,6 +65,7 @@ public class AzureBlobClientLiveTest { protected AzureBlobClient connection; private String containerPrefix = System.getProperty("user.name") + "-azureblob"; + private EncryptionService encryptionService = new JCEEncryptionService(); @BeforeGroups(groups = { "live" }) public void setupClient() { @@ -234,8 +236,8 @@ public class AzureBlobClientLiveTest { object.getProperties().getMetadata().put("Metadata", "metadata-value"); byte[] md5 = object.getProperties().getContentMD5(); String newEtag = connection.putBlob(privateContainer, object); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getProperties() - .getContentMD5())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(object + .getProperties().getContentMD5())); // Test HEAD of missing object try { @@ -257,8 +259,8 @@ public class AzureBlobClientLiveTest { // assertEquals(metadata.getSize(), data.length()); assertEquals(metadata.getContentType(), "text/plain"); // Azure doesn't return the Content-MD5 on head request... - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getProperties() - .getContentMD5())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(object + .getProperties().getContentMD5())); assertEquals(metadata.getETag(), newEtag); assertEquals(metadata.getMetadata().entrySet().size(), 1); assertEquals(metadata.getMetadata().get("metadata"), "metadata-value"); @@ -283,8 +285,8 @@ public class AzureBlobClientLiveTest { // TODO assertEquals(getBlob.getName(), object.getProperties().getName()); assertEquals(getBlob.getContentLength(), new Long(data.length())); assertEquals(getBlob.getProperties().getContentType(), "text/plain"); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getProperties() - .getContentMD5())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(getBlob + .getProperties().getContentMD5())); assertEquals(newEtag, getBlob.getProperties().getETag()); // wait until we can update metadata // assertEquals(getBlob.getProperties().getMetadata().entries().size(), 2); @@ -314,8 +316,8 @@ public class AzureBlobClientLiveTest { object.setData(bais); object.setContentLength(new Long(data.getBytes().length)); newEtag = connection.putBlob(privateContainer, object); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getProperties() - .getContentMD5())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(getBlob + .getProperties().getContentMD5())); // Test GET with options // Non-matching ETag diff --git a/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientTest.java b/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientTest.java index d7103f7d47..166789982d 100644 --- a/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientTest.java +++ b/azure/src/test/java/org/jclouds/azure/storage/blob/AzureBlobClientTest.java @@ -49,7 +49,6 @@ import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; -import org.jclouds.http.HttpUtils; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReturnTrueIf2xx; @@ -61,6 +60,7 @@ import org.jclouds.rest.config.RestModule; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Jsr330; +import org.jclouds.util.internal.Base64; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -356,7 +356,7 @@ public class AzureBlobClientTest { "myaccount"); bindConstant().annotatedWith( Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to( - HttpUtils.toBase64String("key".getBytes())); + Base64.encodeBytes("key".getBytes())); bindConstant().annotatedWith( Jsr330.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to( "x-ms-meta-"); diff --git a/azure/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobClientModuleTest.java b/azure/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobClientModuleTest.java index 04f71f4b86..76c1406619 100644 --- a/azure/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobClientModuleTest.java +++ b/azure/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobClientModuleTest.java @@ -31,12 +31,12 @@ import org.jclouds.azure.storage.handlers.ParseAzureStorageErrorFromXmlContent; import org.jclouds.azure.storage.reference.AzureStorageConstants; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; -import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.config.ParserModule; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.handlers.RedirectionRetryHandler; import org.jclouds.util.Jsr330; +import org.jclouds.util.internal.Base64; import org.testng.annotations.Test; import com.google.inject.AbstractModule; @@ -58,7 +58,7 @@ public class RestAzureBlobClientModuleTest { Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_ACCOUNT)).to("user"); bindConstant() .annotatedWith(Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_KEY)).to( - HttpUtils.toBase64String("secret".getBytes())); + Base64.encodeBytes("secret".getBytes())); bindConstant().annotatedWith( Jsr330.named(AzureBlobConstants.PROPERTY_AZUREBLOB_ENDPOINT)).to( "http://localhost"); diff --git a/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyAuthenticationTest.java b/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyAuthenticationTest.java index d9e30a312b..7ccb55fa06 100755 --- a/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyAuthenticationTest.java +++ b/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyAuthenticationTest.java @@ -36,9 +36,9 @@ import org.jclouds.azure.storage.reference.AzureStorageConstants; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.config.ParserModule; import org.jclouds.util.Jsr330; +import org.jclouds.util.internal.Base64; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -50,7 +50,7 @@ import com.google.inject.Injector; @Test(groups = "unit", testName = "azurestorage.SharedKeyAuthenticationTest") public class SharedKeyAuthenticationTest { - private static final String KEY = HttpUtils.toBase64String("bar".getBytes()); + private static final String KEY = Base64.encodeBytes("bar".getBytes()); private static final String ACCOUNT = "foo"; private Injector injector; private SharedKeyAuthentication filter; diff --git a/azure/src/test/java/org/jclouds/azure/storage/queue/AzureQueueClientTest.java b/azure/src/test/java/org/jclouds/azure/storage/queue/AzureQueueClientTest.java index 57ea8b0ab1..55fb20dd38 100644 --- a/azure/src/test/java/org/jclouds/azure/storage/queue/AzureQueueClientTest.java +++ b/azure/src/test/java/org/jclouds/azure/storage/queue/AzureQueueClientTest.java @@ -40,7 +40,6 @@ import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.azure.storage.reference.AzureStorageConstants; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; -import org.jclouds.http.HttpUtils; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReturnTrueIf2xx; @@ -50,6 +49,7 @@ import org.jclouds.rest.config.RestModule; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Jsr330; +import org.jclouds.util.internal.Base64; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -179,7 +179,7 @@ public class AzureQueueClientTest { Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT)).to("user"); bindConstant().annotatedWith( Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to( - HttpUtils.toBase64String("key".getBytes())); + Base64.encodeBytes("key".getBytes())); bind(Logger.LoggerFactory.class).toInstance(new LoggerFactory() { public Logger getLogger(String category) { return Logger.NULL; diff --git a/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntity.java b/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntity.java index f93fab7dee..efd97a7397 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntity.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntity.java @@ -25,14 +25,25 @@ package org.jclouds.blobstore.binders; import static com.google.common.base.Preconditions.checkNotNull; +import javax.inject.Inject; import javax.ws.rs.core.HttpHeaders; import org.jclouds.blobstore.domain.Blob; import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpUtils; import org.jclouds.rest.Binder; +import org.jclouds.util.EncryptionService; +/** + * + * @author Adrian Cole + */ public class BindBlobToEntity implements Binder { + private final EncryptionService encryptionService; + + @Inject + public BindBlobToEntity(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } public void bindToRequest(HttpRequest request, Object entity) { Blob object = (Blob) entity; @@ -48,7 +59,7 @@ public class BindBlobToEntity implements Binder { if (object.getMetadata().getContentMD5() != null) { request.getHeaders().put("Content-MD5", - HttpUtils.toBase64String(object.getMetadata().getContentMD5())); + encryptionService.toBase64String(object.getMetadata().getContentMD5())); } } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntityAndUserMetadataToHeadersWithPrefix.java b/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntityAndUserMetadataToHeadersWithPrefix.java index 0482a914fb..d514c52dd1 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntityAndUserMetadataToHeadersWithPrefix.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/binders/BindBlobToEntityAndUserMetadataToHeadersWithPrefix.java @@ -30,13 +30,20 @@ import javax.inject.Named; import org.jclouds.blobstore.domain.Blob; import org.jclouds.http.HttpRequest; +import org.jclouds.util.EncryptionService; +/** + * + * @author Adrian Cole + */ public class BindBlobToEntityAndUserMetadataToHeadersWithPrefix extends BindBlobToEntity { private final String metadataPrefix; @Inject public BindBlobToEntityAndUserMetadataToHeadersWithPrefix( - @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) { + @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix, + EncryptionService encryptionService) { + super(encryptionService); this.metadataPrefix = metadataPrefix; } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5.java index 958b1b13aa..6cdd0fafe0 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5.java @@ -23,7 +23,7 @@ */ package org.jclouds.blobstore.functions; -import org.jclouds.blobstore.functions.impl.BouncyCastleGenerateMD5; +import org.jclouds.blobstore.functions.impl.JCEGenerateMD5; import com.google.common.base.Function; import com.google.inject.ImplementedBy; @@ -32,7 +32,7 @@ import com.google.inject.ImplementedBy; * * @author Adrian Cole */ -@ImplementedBy(BouncyCastleGenerateMD5.class) +@ImplementedBy(JCEGenerateMD5.class) public interface GenerateMD5 extends Function { } \ No newline at end of file diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5Result.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5Result.java index cc6c9ed9c2..528f3b57e4 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5Result.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/GenerateMD5Result.java @@ -26,7 +26,7 @@ package org.jclouds.blobstore.functions; import java.io.InputStream; import org.jclouds.blobstore.domain.MD5InputStreamResult; -import org.jclouds.blobstore.functions.impl.BouncyCastleGenerateMD5Result; +import org.jclouds.blobstore.functions.impl.JCEGenerateMD5Result; import com.google.common.base.Function; import com.google.inject.ImplementedBy; @@ -35,7 +35,7 @@ import com.google.inject.ImplementedBy; * * @author Adrian Cole */ -@ImplementedBy(BouncyCastleGenerateMD5Result.class) +@ImplementedBy(JCEGenerateMD5Result.class) public interface GenerateMD5Result extends Function { } \ No newline at end of file diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/ParseSystemAndUserMetadataFromHeaders.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/ParseSystemAndUserMetadataFromHeaders.java index 434e942e51..9b51a5bb60 100755 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/ParseSystemAndUserMetadataFromHeaders.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/ParseSystemAndUserMetadataFromHeaders.java @@ -35,10 +35,10 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.blobstore.domain.MutableBlobMetadata; import org.jclouds.http.HttpException; import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpUtils; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.util.DateService; +import org.jclouds.util.EncryptionService; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; @@ -51,14 +51,17 @@ public class ParseSystemAndUserMetadataFromHeaders implements private final String metadataPrefix; private final DateService dateParser; private final Provider metadataFactory; + private final EncryptionService encryptionService; private GeneratedHttpRequest request; @Inject public ParseSystemAndUserMetadataFromHeaders(Provider metadataFactory, - DateService dateParser, @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) { + DateService dateParser, @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix, + EncryptionService encryptionService) { this.metadataFactory = metadataFactory; this.dateParser = dateParser; this.metadataPrefix = metadataPrefix; + this.encryptionService = encryptionService; } public MutableBlobMetadata apply(HttpResponse from) { @@ -115,7 +118,7 @@ public class ParseSystemAndUserMetadataFromHeaders implements protected void addContentMD5To(HttpResponse from, MutableBlobMetadata metadata) { String contentMD5 = from.getFirstHeaderOrNull("Content-MD5"); if (contentMD5 != null) { - metadata.setContentMD5(HttpUtils.fromBase64String(contentMD5)); + metadata.setContentMD5(encryptionService.fromBase64String(contentMD5)); } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5.java similarity index 81% rename from blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5.java rename to blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5.java index 108f21ef8b..91bbcd808f 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5.java @@ -26,17 +26,19 @@ package org.jclouds.blobstore.functions.impl; import javax.inject.Singleton; import org.jclouds.blobstore.functions.GenerateMD5; -import org.jclouds.http.HttpUtils; +import org.jclouds.util.EncryptionService; +import org.jclouds.util.internal.JCEEncryptionService; /** * * @author Adrian Cole */ @Singleton -public class BouncyCastleGenerateMD5 implements GenerateMD5 { +public class JCEGenerateMD5 implements GenerateMD5 { + private static final EncryptionService encryptionService = new JCEEncryptionService(); public byte[] apply(Object from) { - return HttpUtils.md5(from); + return encryptionService.md5(from); } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5Result.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5Result.java similarity index 54% rename from blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5Result.java rename to blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5Result.java index 15ae3242cd..30f0d18f86 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/BouncyCastleGenerateMD5Result.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/impl/JCEGenerateMD5Result.java @@ -23,47 +23,26 @@ */ package org.jclouds.blobstore.functions.impl; -import java.io.IOException; import java.io.InputStream; import javax.inject.Singleton; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.bouncycastle.crypto.digests.MD5Digest; import org.jclouds.blobstore.domain.MD5InputStreamResult; import org.jclouds.blobstore.functions.GenerateMD5Result; -import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.util.EncryptionService; +import org.jclouds.util.internal.JCEEncryptionService; /** * * @author Adrian Cole */ @Singleton -public class BouncyCastleGenerateMD5Result implements GenerateMD5Result { +public class JCEGenerateMD5Result implements GenerateMD5Result { + private static final EncryptionService encryptionService = new JCEEncryptionService(); + public MD5InputStreamResult apply(InputStream toEncode) { - MD5Digest eTag = new MD5Digest(); - byte[] resBuf = new byte[eTag.getDigestSize()]; - byte[] buffer = new byte[1024]; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - long length = 0; - int numRead = -1; - try { - do { - numRead = toEncode.read(buffer); - if (numRead > 0) { - length += numRead; - eTag.update(buffer, 0, numRead); - out.write(buffer, 0, numRead); - } - } while (numRead != -1); - } catch (IOException e) { - throw new BlobRuntimeException("couldn't get MD5 for: " + toEncode, e); - } finally { - IOUtils.closeQuietly(out); - IOUtils.closeQuietly(toEncode); - } - eTag.doFinal(resBuf, 0); - return new MD5InputStreamResult(out.toByteArray(), resBuf, length); + org.jclouds.util.EncryptionService.MD5InputStreamResult result = encryptionService + .generateMD5Result(toEncode); + return new MD5InputStreamResult(result.data, result.md5, result.length); } } diff --git a/blobstore/src/test/java/org/jclouds/blobstore/functions/ParseBlobMetadataFromHeadersTest.java b/blobstore/src/test/java/org/jclouds/blobstore/functions/ParseBlobMetadataFromHeadersTest.java index d4b59fd549..f1ad4149b7 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/functions/ParseBlobMetadataFromHeadersTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/functions/ParseBlobMetadataFromHeadersTest.java @@ -40,6 +40,7 @@ import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl; import org.jclouds.http.HttpException; import org.jclouds.http.HttpResponse; import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.util.internal.JCEEncryptionService; import org.jclouds.util.internal.SimpleDateFormatDateService; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -62,7 +63,7 @@ public class ParseBlobMetadataFromHeadersTest { void setUp() { parser = new ParseSystemAndUserMetadataFromHeaders(blobMetadataProvider, - new SimpleDateFormatDateService(), "prefix"); + new SimpleDateFormatDateService(), "prefix", new JCEEncryptionService()); GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class); expect(request.getEndpoint()).andReturn(URI.create("http://localhost/key")).anyTimes(); diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java index 32efa4a542..64f4b5e76f 100755 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java @@ -46,7 +46,7 @@ import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ResourceMetadata; import org.jclouds.blobstore.util.BlobStoreUtils; import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -329,7 +329,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest< // NOTE all metadata in jclouds comes out as lowercase, in an effort to normalize the // providers. object.getMetadata().getUserMetadata().put("Adrian", "powderpuff"); - object.getMetadata().setContentMD5(HttpUtils.md5(TEST_STRING.getBytes())); + object.getMetadata().setContentMD5(new JCEEncryptionService().md5(TEST_STRING.getBytes())); String containerName = getContainerName(); try { addBlobToContainer(containerName, object); @@ -356,7 +356,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest< assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType(); assertEquals(metadata.getSize(), new Long(TEST_STRING.length())); assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff"); - assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes())); + assertEquals(metadata.getContentMD5(), new JCEEncryptionService().md5(TEST_STRING.getBytes())); } } \ No newline at end of file diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java index fd4465fa0f..d61f70a189 100755 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java @@ -31,7 +31,7 @@ import java.net.URL; import java.net.URLConnection; import org.jclouds.blobstore.domain.Blob; -import org.jclouds.http.HttpUtils; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; @@ -64,7 +64,7 @@ public class BaseBlobLiveTest extends BaseBlobStoreIntegrationTest { String key = "hello"; URL url = new URL(httpStreamUrl); - byte[] md5 = HttpUtils.fromHexString(httpStreamETag); + byte[] md5 = new JCEEncryptionService().fromHexString(httpStreamETag); URLConnection connection = url.openConnection(); int length = connection.getContentLength(); diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/StubAsyncBlobStore.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/StubAsyncBlobStore.java index 59169f40b0..931aeb2aa8 100755 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/StubAsyncBlobStore.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/StubAsyncBlobStore.java @@ -82,9 +82,9 @@ import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.util.DateService; +import org.jclouds.util.EncryptionService; import org.jclouds.util.Utils; import com.google.common.base.Function; @@ -105,6 +105,7 @@ import com.google.inject.internal.Nullable; public class StubAsyncBlobStore implements AsyncBlobStore { protected final DateService dateService; + protected final EncryptionService encryptionService; private final ConcurrentMap> containerToBlobs; protected final Blob.Factory blobProvider; protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; @@ -117,11 +118,12 @@ public class StubAsyncBlobStore implements AsyncBlobStore { @Inject protected StubAsyncBlobStore( ConcurrentMap> containerToBlobs, - DateService dateService, Blob.Factory blobProvider, - GetDirectoryStrategy getDirectoryStrategy, MkdirStrategy mkdirStrategy, - IsDirectoryStrategy isDirectoryStrategy, + DateService dateService, EncryptionService encryptionService, + Blob.Factory blobProvider, GetDirectoryStrategy getDirectoryStrategy, + MkdirStrategy mkdirStrategy, IsDirectoryStrategy isDirectoryStrategy, HttpGetOptionsListToGetOptions httpGetOptionsConverter, ExecutorService service) { this.dateService = checkNotNull(dateService, "dateService"); + this.encryptionService = checkNotNull(encryptionService, "encryptionService"); this.containerToBlobs = checkNotNull(containerToBlobs, "containerToBlobs"); this.blobProvider = checkNotNull(blobProvider, "blobProvider"); this.getDirectoryStrategy = checkNotNull(getDirectoryStrategy, "getDirectoryStrategy"); @@ -514,8 +516,8 @@ public class StubAsyncBlobStore implements AsyncBlobStore { object.getMetadata().setSize(data.length); MutableBlobMetadata newMd = copy(object.getMetadata()); newMd.setLastModified(new Date()); - final byte[] md5 = HttpUtils.md5(data); - final String eTag = HttpUtils.toHexString(md5); + final byte[] md5 = encryptionService.md5(data); + final String eTag = encryptionService.toHexString(md5); newMd.setETag(eTag); newMd.setContentMD5(md5); newMd.setContentType(object.getMetadata().getContentType()); diff --git a/core/pom.xml b/core/pom.xml index f5f6329504..091636b013 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -94,11 +94,6 @@ 7.0.0pre3 test - - org.bouncycastle - bcprov-jdk15 - 1.44 - com.google.code.gson gson diff --git a/core/src/main/java/org/jclouds/http/HttpUtils.java b/core/src/main/java/org/jclouds/http/HttpUtils.java index 0e830e08a7..6b5fa0d066 100644 --- a/core/src/main/java/org/jclouds/http/HttpUtils.java +++ b/core/src/main/java/org/jclouds/http/HttpUtils.java @@ -23,13 +23,10 @@ */ package org.jclouds.http; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -37,9 +34,6 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.net.URLEncoder; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -49,16 +43,7 @@ import java.util.regex.Pattern; import javax.annotation.Resource; import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.digests.MD5Digest; -import org.bouncycastle.crypto.digests.SHA1Digest; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.macs.HMac; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.util.encoders.Base64; import org.jclouds.logging.Logger; -import org.jclouds.util.Utils; import com.google.common.base.Function; import com.google.common.base.Joiner; @@ -73,6 +58,18 @@ public class HttpUtils { public static final Pattern URI_PATTERN = Pattern.compile("([a-z0-9]+)://([^:]*):(.*)@(.*)"); public static final Pattern PATTERN_THAT_BREAKS_URI = Pattern.compile("[a-z0-9]+://.*/.*@.*"); // slash + public static Long calculateSize(Object data) { + Long size = null; + if (data instanceof byte[]) { + size = new Long(((byte[]) data).length); + } else if (data instanceof String) { + size = new Long(((String) data).length()); + } else if (data instanceof File) { + size = ((File) data).length(); + } + return size; + } + @Resource protected static Logger logger = Logger.NULL; @@ -198,25 +195,6 @@ public class HttpUtils { public static final Pattern IP_PATTERN = Pattern .compile("b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).)" + "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)b"); - static final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', - (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', - (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; - - public static String toHexString(byte[] raw) { - byte[] hex = new byte[2 * raw.length]; - int index = 0; - - for (byte b : raw) { - int v = b & 0xFF; - hex[index++] = HEX_CHAR_TABLE[v >>> 4]; - hex[index++] = HEX_CHAR_TABLE[v & 0xF]; - } - try { - return new String(hex, "ASCII"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } public static void logRequest(Logger logger, HttpRequest request, String prefix) { if (logger.isDebugEnabled()) { @@ -237,165 +215,6 @@ public class HttpUtils { } } - public static byte[] fromHexString(String hex) { - if (hex.startsWith("0x")) - hex = hex.substring(2); - byte[] bytes = new byte[hex.length() / 2]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); - } - return bytes; - } - - public static String hmacSha256Base64(String toEncode, byte[] key) - throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException { - Digest digest = new SHA256Digest(); - return hmacBase64(toEncode, key, digest); - } - - private static String hmacBase64(String toEncode, byte[] key, Digest digest) { - byte[] resBuf = hmac(toEncode, key, digest); - return toBase64String(resBuf); - } - - public static byte[] hmac(String toEncode, byte[] key, Digest digest) { - HMac hmac = new HMac(digest); - byte[] resBuf = new byte[hmac.getMacSize()]; - byte[] plainBytes = Utils.encodeString(toEncode); - byte[] keyBytes = key; - hmac.init(new KeyParameter(keyBytes)); - hmac.update(plainBytes, 0, plainBytes.length); - hmac.doFinal(resBuf, 0); - return resBuf; - } - - public static String hmacSha1Base64(String toEncode, byte[] key) - throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException { - Digest digest = new SHA1Digest(); - return hmacBase64(toEncode, key, digest); - } - - public static String md5Hex(byte[] toEncode) throws NoSuchAlgorithmException, - NoSuchProviderException, InvalidKeyException, UnsupportedEncodingException { - byte[] resBuf = md5(toEncode); - return toHexString(resBuf); - } - - public static String md5Base64(byte[] toEncode) throws NoSuchAlgorithmException, - NoSuchProviderException, InvalidKeyException { - byte[] resBuf = md5(toEncode); - return toBase64String(resBuf); - } - - public static byte[] md5(byte[] plainBytes) { - MD5Digest eTag = new MD5Digest(); - byte[] resBuf = new byte[eTag.getDigestSize()]; - eTag.update(plainBytes, 0, plainBytes.length); - eTag.doFinal(resBuf, 0); - return resBuf; - } - - public static byte[] md5(File toEncode) { - MD5Digest eTag = new MD5Digest(); - byte[] resBuf = new byte[eTag.getDigestSize()]; - byte[] buffer = new byte[1024]; - int numRead = -1; - InputStream i = null; - try { - i = new FileInputStream(toEncode); - do { - numRead = i.read(buffer); - if (numRead > 0) { - eTag.update(buffer, 0, numRead); - } - } while (numRead != -1); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeQuietly(i); - } - eTag.doFinal(resBuf, 0); - return resBuf; - } - - public static String toBase64String(byte[] resBuf) { - return new String(Base64.encode(resBuf)); - } - - public static Long calculateSize(Object data) { - Long size = null; - if (data instanceof byte[]) { - size = new Long(((byte[]) data).length); - } else if (data instanceof String) { - size = new Long(((String) data).length()); - } else if (data instanceof File) { - size = ((File) data).length(); - } - return size; - } - - /** - * @throws IOException - */ - public static byte[] md5(Object data) { - checkNotNull(data, "data must be set before calling generateETag()"); - byte[] md5 = null; - if (data == null) { - } else if (data instanceof byte[]) { - md5 = md5((byte[]) data); - } else if (data instanceof String) { - md5 = md5(((String) data).getBytes()); - } else if (data instanceof File) { - md5 = md5(((File) data)); - } else if (data instanceof InputStream) { - md5 = generateMD5Result(((InputStream) data)).md5; - } else { - throw new UnsupportedOperationException("Content not supported " + data.getClass()); - } - return md5; - - } - - public static MD5InputStreamResult generateMD5Result(InputStream toEncode) { - MD5Digest eTag = new MD5Digest(); - byte[] resBuf = new byte[eTag.getDigestSize()]; - byte[] buffer = new byte[1024]; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - long length = 0; - int numRead = -1; - try { - do { - numRead = toEncode.read(buffer); - if (numRead > 0) { - length += numRead; - eTag.update(buffer, 0, numRead); - out.write(buffer, 0, numRead); - } - } while (numRead != -1); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeQuietly(out); - IOUtils.closeQuietly(toEncode); - } - eTag.doFinal(resBuf, 0); - return new MD5InputStreamResult(out.toByteArray(), resBuf, length); - } - - public static class MD5InputStreamResult { - public final byte[] data; - public final byte[] md5; - public final long length; - - MD5InputStreamResult(byte[] data, byte[] eTag, long length) { - this.data = checkNotNull(data, "data"); - this.md5 = checkNotNull(eTag, "eTag"); - checkArgument(length >= 0, "length cannot me negative"); - this.length = length; - } - - } - public static void copy(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[1024]; long length = 0; @@ -425,8 +244,4 @@ public class HttpUtils { return buffer.toString(); } - public static byte[] fromBase64String(String encoded) { - return Base64.decode(encoded); - } - } diff --git a/core/src/main/java/org/jclouds/http/filters/BasicAuthentication.java b/core/src/main/java/org/jclouds/http/filters/BasicAuthentication.java index 9ab91b732a..4f662cb57a 100644 --- a/core/src/main/java/org/jclouds/http/filters/BasicAuthentication.java +++ b/core/src/main/java/org/jclouds/http/filters/BasicAuthentication.java @@ -35,7 +35,7 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpException; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequestFilter; -import org.jclouds.http.HttpUtils; +import org.jclouds.util.EncryptionService; /** * Uses Basic Authentication to sign the request. @@ -47,12 +47,14 @@ import org.jclouds.http.HttpUtils; @Singleton public class BasicAuthentication implements HttpRequestFilter { - private List credentialList; + private final List credentialList; - public BasicAuthentication(String user, String password) throws UnsupportedEncodingException { + public BasicAuthentication(String user, String password, EncryptionService encryptionService) + throws UnsupportedEncodingException { this.credentialList = Collections.singletonList("Basic " - + HttpUtils.toBase64String(String.format("%s:%s", checkNotNull(user, "user"), - checkNotNull(password, "password")).getBytes("UTF-8"))); + + encryptionService.toBase64String(String.format("%s:%s", + checkNotNull(user, "user"), checkNotNull(password, "password")).getBytes( + "UTF-8"))); } public void filter(HttpRequest request) throws HttpException { diff --git a/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java b/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java index 0eb8d2f091..413e372799 100644 --- a/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java +++ b/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java @@ -27,17 +27,18 @@ import javax.annotation.Resource; import org.apache.commons.io.IOUtils; import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpUtils; import org.jclouds.logging.Logger; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.util.internal.Base64; import com.google.common.base.Function; /** * @author Adrian Cole */ -public class ParseContentMD5FromHeaders implements Function, InvocationContext { +public class ParseContentMD5FromHeaders implements Function, + InvocationContext { public static class NoContentMD5Exception extends RuntimeException { @@ -69,7 +70,7 @@ public class ParseContentMD5FromHeaders implements Function + * + * ==================================================================== + * 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.jclouds.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import org.jclouds.util.internal.JCEEncryptionService; + +import com.google.inject.ImplementedBy; + +/** + * + * @author Adrian Cole + */ +@ImplementedBy(JCEEncryptionService.class) +public interface EncryptionService { + + String toHexString(byte[] raw); + + byte[] fromHexString(String hex); + + String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException; + + String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException; + + String md5Hex(byte[] toEncode) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, UnsupportedEncodingException; + + String md5Base64(byte[] toEncode) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException; + + byte[] md5(byte[] plainBytes); + + byte[] md5(File toEncode); + + String toBase64String(byte[] resBuf); + + byte[] md5(Object data); + + MD5InputStreamResult generateMD5Result(InputStream toEncode); + + public static class MD5InputStreamResult { + public final byte[] data; + public final byte[] md5; + public final long length; + + public MD5InputStreamResult(byte[] data, byte[] eTag, long length) { + this.data = checkNotNull(data, "data"); + this.md5 = checkNotNull(eTag, "eTag"); + checkArgument(length >= 0, "length cannot me negative"); + this.length = length; + } + + } + + byte[] fromBase64String(String encoded); + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/util/internal/Base64.java b/core/src/main/java/org/jclouds/util/internal/Base64.java new file mode 100644 index 0000000000..94343d8f39 --- /dev/null +++ b/core/src/main/java/org/jclouds/util/internal/Base64.java @@ -0,0 +1,1459 @@ +// +// NOTE: The following source code is the iHarder.net public domain +// Base64 library and is provided here as a convenience. For updates, +// problems, questions, etc. regarding this code, please visit: +// http://iharder.sourceforge.net/current/java/base64/ +// + +package org.jclouds.util.internal; + + +/** + * Encodes and decodes to and from Base64 notation. + * + *

+ * Change Log: + *

+ *
    + *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
  • + *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
  • + *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
  • + *
  • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
  • + *
  • v1.4 - Added helper methods to read/write files.
  • + *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • + *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
  • + *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • + *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • + *
+ * + *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.1 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + + /** The 64 valid Base64 values. */ + private final static byte[] ALPHABET; + private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + /** Determine which ALPHABET to use. */ + static + { + byte[] __bytes; + try + { + __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException use) + { + __bytes = _NATIVE_ALPHABET; // Fall back to native encoding + } // end catch + ALPHABET = __bytes; + } // end static + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + // I think I end up not using the BAD_ENCODING indicator. + //private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + + /** Defeats instantiation. */ + private Base64(){} + + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); + return b4; + } // end encode3to4 + + + /** + * Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset ) + { + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) + { + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + return -1; + } //e nd catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len ) + { + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + catch( java.io.IOException e ) + { + + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + catch( java.io.IOException e ) + { + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); + return null; + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error decoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error encoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0 ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0 ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/core/src/main/java/org/jclouds/util/internal/BaseEncryptionService.java b/core/src/main/java/org/jclouds/util/internal/BaseEncryptionService.java new file mode 100755 index 0000000000..a795050e3d --- /dev/null +++ b/core/src/main/java/org/jclouds/util/internal/BaseEncryptionService.java @@ -0,0 +1,85 @@ +package org.jclouds.util.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import org.jclouds.util.EncryptionService; + +/** + * + * @author Adrian Cole + */ +public abstract class BaseEncryptionService implements EncryptionService { + + final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', + (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', + (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; + + public String toHexString(byte[] raw) { + byte[] hex = new byte[2 * raw.length]; + int index = 0; + + for (byte b : raw) { + int v = b & 0xFF; + hex[index++] = HEX_CHAR_TABLE[v >>> 4]; + hex[index++] = HEX_CHAR_TABLE[v & 0xF]; + } + try { + return new String(hex, "ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + public byte[] fromHexString(String hex) { + if (hex.startsWith("0x")) + hex = hex.substring(2); + byte[] bytes = new byte[hex.length() / 2]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); + } + return bytes; + } + + public String md5Hex(byte[] toEncode) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, UnsupportedEncodingException { + byte[] resBuf = md5(toEncode); + return toHexString(resBuf); + } + + public String md5Base64(byte[] toEncode) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException { + byte[] resBuf = md5(toEncode); + return toBase64String(resBuf); + } + + /** + * @throws IOException + */ + public byte[] md5(Object data) { + checkNotNull(data, "data must be set before calling generateETag()"); + byte[] md5 = null; + if (data == null) { + } else if (data instanceof byte[]) { + md5 = md5((byte[]) data); + } else if (data instanceof String) { + md5 = md5(((String) data).getBytes()); + } else if (data instanceof File) { + md5 = md5(((File) data)); + } else if (data instanceof InputStream) { + md5 = generateMD5Result(((InputStream) data)).md5; + } else { + throw new UnsupportedOperationException("Content not supported " + data.getClass()); + } + return md5; + + } + +} diff --git a/core/src/main/java/org/jclouds/util/internal/JCEEncryptionService.java b/core/src/main/java/org/jclouds/util/internal/JCEEncryptionService.java new file mode 100644 index 0000000000..28d75dcf74 --- /dev/null +++ b/core/src/main/java/org/jclouds/util/internal/JCEEncryptionService.java @@ -0,0 +1,125 @@ +package org.jclouds.util.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; + +/** + * + * @author Adrian Cole + */ +public class JCEEncryptionService extends BaseEncryptionService { + + public String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException { + return hmacBase64(toEncode, key, "HmacSHA256"); + } + + private String hmacBase64(String toEncode, byte[] key, String algorithm) { + byte[] resBuf = hmac(toEncode, key, algorithm); + return toBase64String(resBuf); + } + + public byte[] hmac(String toEncode, byte[] key, String algorithm) { + SecretKeySpec signingKey = new SecretKeySpec(key, algorithm); + + Mac mac = null; + try { + mac = Mac.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Could not find the " + algorithm + " algorithm", e); + } + try { + mac.init(signingKey); + } catch (InvalidKeyException e) { + throw new RuntimeException("Could not initialize the " + algorithm + " algorithm", e); + } + return mac.doFinal(toEncode.getBytes()); + } + + public String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException { + return hmacBase64(toEncode, key, "HmacSHA1"); + } + + public byte[] md5(byte[] plainBytes) { + MessageDigest eTag = getDigest(); + eTag.update(plainBytes, 0, plainBytes.length); + return eTag.digest(); + } + + public byte[] md5(File toEncode) { + MessageDigest eTag = getDigest(); + byte[] buffer = new byte[1024]; + int numRead = -1; + InputStream i = null; + try { + i = new FileInputStream(toEncode); + do { + numRead = i.read(buffer); + if (numRead > 0) { + eTag.update(buffer, 0, numRead); + } + } while (numRead != -1); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(i); + } + return eTag.digest(); + } + + public String toBase64String(byte[] resBuf) { + return Base64.encodeBytes(resBuf); + } + + public MD5InputStreamResult generateMD5Result(InputStream toEncode) { + MessageDigest eTag = getDigest(); + byte[] buffer = new byte[1024]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + long length = 0; + int numRead = -1; + try { + do { + numRead = toEncode.read(buffer); + if (numRead > 0) { + length += numRead; + eTag.update(buffer, 0, numRead); + out.write(buffer, 0, numRead); + } + } while (numRead != -1); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(out); + IOUtils.closeQuietly(toEncode); + } + return new MD5InputStreamResult(out.toByteArray(), eTag.digest(), length); + } + + private MessageDigest getDigest() { + MessageDigest eTag; + try { + eTag = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Could not find the MD5 algorithm", e); + } + return eTag; + } + + public byte[] fromBase64String(String encoded) { + return Base64.decode(encoded); + } + +} diff --git a/core/src/main/java/org/jclouds/util/internal/SimpleDateFormatDateService.java b/core/src/main/java/org/jclouds/util/internal/SimpleDateFormatDateService.java new file mode 100755 index 0000000000..75d08e8aca --- /dev/null +++ b/core/src/main/java/org/jclouds/util/internal/SimpleDateFormatDateService.java @@ -0,0 +1,172 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.jclouds.util.internal; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.SimpleTimeZone; + +import net.jcip.annotations.GuardedBy; +import net.jcip.annotations.ThreadSafe; + +import org.jclouds.util.DateService; + +/** + * + * uses {@link SimpleDateFormat} internally. + * + * @author Adrian Cole + * @author James Murty + */ +@ThreadSafe +public class SimpleDateFormatDateService implements DateService { + /* + * Use default Java Date/SimpleDateFormat classes for date manipulation, but be *very* careful to + * guard against the lack of thread safety. + */ + @GuardedBy("this") + private static final SimpleDateFormat iso8601SecondsSimpleDateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + + @GuardedBy("this") + private static final SimpleDateFormat iso8601SimpleDateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); + + @GuardedBy("this") + private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat( + "EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + + @GuardedBy("this") + private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat( + "EEE MMM dd HH:mm:ss '+0000' yyyy", Locale.US); + + static { + iso8601SimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); + iso8601SecondsSimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); + rfc822SimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); + cSimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); + } + + public final Date fromSeconds(long seconds) { + return new Date(seconds * 1000); + } + + public final String cDateFormat(Date date) { + synchronized (cSimpleDateFormat) { + return cSimpleDateFormat.format(date); + } + } + + public final String cDateFormat() { + return cDateFormat(new Date()); + } + + public final Date cDateParse(String toParse) { + synchronized (cSimpleDateFormat) { + try { + return cSimpleDateFormat.parse(toParse); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + } + + public final String rfc822DateFormat(Date date) { + synchronized (rfc822SimpleDateFormat) { + return rfc822SimpleDateFormat.format(date); + } + } + + public final String rfc822DateFormat() { + return rfc822DateFormat(new Date()); + } + + public final Date rfc822DateParse(String toParse) { + synchronized (rfc822SimpleDateFormat) { + try { + return rfc822SimpleDateFormat.parse(toParse); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + } + + public final String iso8601SecondsDateFormat() { + return iso8601SecondsDateFormat(new Date()); + } + + public final String iso8601DateFormat(Date date) { + synchronized (iso8601SimpleDateFormat) { + return iso8601SimpleDateFormat.format(date); + } + } + + public final String iso8601DateFormat() { + return iso8601DateFormat(new Date()); + } + + public final Date iso8601DateParse(String toParse) { + toParse = trimNanosToMillis(toParse); + synchronized (iso8601SimpleDateFormat) { + try { + return iso8601SimpleDateFormat.parse(toParse); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + } + + private String trimNanosToMillis(String toParse) { + if (toParse.matches(".*[0-9][0-9][0-9][0-9][0-9][0-9]")) + toParse = toParse.substring(0, toParse.length() - 3) + 'Z'; + return toParse; + } + + private String trimTZ(String toParse) { + if (toParse.length() == 25 && toParse.matches(".*[0-2][0-9]:00")) + toParse = toParse.substring(0, toParse.length() - 6) + 'Z'; + return toParse; + } + + public final Date iso8601SecondsDateParse(String toParse) { + toParse = trimTZ(toParse); + synchronized (iso8601SecondsSimpleDateFormat) { + try { + return iso8601SecondsSimpleDateFormat.parse(toParse); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public String iso8601SecondsDateFormat(Date date) { + synchronized (iso8601SecondsSimpleDateFormat) { + return iso8601SecondsSimpleDateFormat.format(date); + } + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java b/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java index 73fd8016e4..3b326838a4 100755 --- a/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java +++ b/core/src/test/java/org/jclouds/http/filters/BasicAuthenticationTest.java @@ -31,6 +31,7 @@ import java.net.URI; import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpRequest; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.Test; /** @@ -45,7 +46,7 @@ public class BasicAuthenticationTest { public void testAuth() throws UnsupportedEncodingException { - BasicAuthentication filter = new BasicAuthentication(USER, PASSWORD); + BasicAuthentication filter = new BasicAuthentication(USER, PASSWORD, new JCEEncryptionService()); HttpRequest request = new HttpRequest("GET", URI.create("http://localhost")); filter.filter(request); assertEquals(request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION), diff --git a/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java b/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java index ec68dd9943..2746d8f5ee 100644 --- a/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java +++ b/core/src/test/java/org/jclouds/http/internal/WireLiveTest.java @@ -39,9 +39,9 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.jclouds.concurrent.WithinThreadExecutorService; -import org.jclouds.http.HttpUtils; -import org.jclouds.http.internal.HttpWire; import org.jclouds.logging.Logger; +import org.jclouds.util.EncryptionService; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.Test; /** @@ -54,6 +54,8 @@ public class WireLiveTest { private static final String sysHttpStreamUrl = System.getProperty("jclouds.wire.httpstream.url"); private static final String sysHttpStreamMd5 = System.getProperty("jclouds.wire.httpstream.md5"); + private static final EncryptionService encryptionService = new JCEEncryptionService(); + private static class ConnectionTester implements Callable { private final InputStream fromServer; @@ -66,9 +68,9 @@ public class WireLiveTest { InputStream in = wire.input(fromServer); ByteArrayOutputStream out = new ByteArrayOutputStream(); IOUtils.copy(in, out); - byte[] compare = HttpUtils.md5(new ByteArrayInputStream(out.toByteArray())); + byte[] compare = encryptionService.md5(new ByteArrayInputStream(out.toByteArray())); Thread.sleep(100); - assertEquals(HttpUtils.toHexString(compare), checkNotNull(sysHttpStreamMd5, + assertEquals(encryptionService.toHexString(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5)); assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484); return null; @@ -148,9 +150,10 @@ public class WireLiveTest { URLConnection connection = url.openConnection(); HttpWire wire = setUp(); InputStream in = wire.input(connection.getInputStream()); - byte[] compare = HttpUtils.md5(in); + byte[] compare = encryptionService.md5(in); Thread.sleep(100); - assertEquals(HttpUtils.toHexString(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5)); + assertEquals(encryptionService.toHexString(compare), checkNotNull(sysHttpStreamMd5, + sysHttpStreamMd5)); assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484); } @@ -169,9 +172,10 @@ public class WireLiveTest { URLConnection connection = url.openConnection(); HttpWire wire = setUpSynch(); InputStream in = wire.input(connection.getInputStream()); - byte[] compare = HttpUtils.md5(in); + byte[] compare = encryptionService.md5(in); Thread.sleep(100); - assertEquals(HttpUtils.toHexString(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5)); + assertEquals(encryptionService.toHexString(compare), checkNotNull(sysHttpStreamMd5, + sysHttpStreamMd5)); assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484); } diff --git a/core/src/test/java/org/jclouds/util/EncryptionServiceTest.java b/core/src/test/java/org/jclouds/util/EncryptionServiceTest.java new file mode 100644 index 0000000000..6de7327151 --- /dev/null +++ b/core/src/test/java/org/jclouds/util/EncryptionServiceTest.java @@ -0,0 +1,148 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.jclouds.util; + +import static org.testng.Assert.assertEquals; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; + +import org.jclouds.PerformanceTest; +import org.jclouds.util.internal.Base64; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * This tests the performance of Digest commands. + * + * @author Adrian Cole + */ +@Test(groups = "performance", sequential = true, testName = "jclouds.encryptionService") +public class EncryptionServiceTest extends PerformanceTest { + + protected EncryptionService encryptionService; + + @BeforeTest + protected void createEncryptionService() { + Injector i = Guice.createInjector(); + encryptionService = i.getInstance(EncryptionService.class); + } + + @Test(dataProvider = "hmacsha1") + void testDigestSerialResponseTime(byte[] key, String message, String base64Digest) + throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException { + for (int i = 0; i < 10000; i++) + testHmacSha1Base64(key, message, base64Digest); + } + + @Test(dataProvider = "hmacsha1") + void testDigestParallelResponseTime(final byte[] key, final String message, + final String base64Digest) throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidKeyException, InterruptedException, ExecutionException { + CompletionService completer = new ExecutorCompletionService(exec); + for (int i = 0; i < 10000; i++) + completer.submit(new Callable() { + public Boolean call() throws Exception { + testHmacSha1Base64(key, message, base64Digest); + return true; + } + }); + for (int i = 0; i < 10000; i++) + assert completer.take().get(); + } + + @DataProvider(name = "eTag") + public Object[][] createMD5Data() { + return base64MD5MessageDigest; + } + + public final static Object[][] base64MD5MessageDigest = { + { "apple", "1f3870be274f6c49b3e31a0c6728957f" }, + { "bear", "893b56e3cfe153fb770a120b83bac20c" }, + { "candy", "c48ba993d35c3abe0380f91738fe2a34" }, + { "dogma", "95eb470e4faee302e9cd3063b1923dab" }, + { "emma", "00a809937eddc44521da9521269e75c6" } }; + + public final static Object[][] base64KeyMessageDigest = { + { Base64.decode("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", + "thcxhlUFcmTii8C2+zeMjvFGvgA=" }, + { Base64.decode("SmVmZQ=="), "what do ya want for nothing?", + "7/zfauXrL6LSdBbV8YTfnCWafHk=" }, + { Base64.decode("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", + "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" }, + { + Base64 + .decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="), + "Test Using Larger Than Block-Size Key - Hash Key First", + "qkrl4VJy0A6VcFY3zoo7Ve1AIRI=" }, + { + Base64 + .decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="), + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + "6OmdD0UjfXhta7qnllx4CLv/GpE=" } }; + + @DataProvider(name = "hmacsha1") + public Object[][] createData1() { + return base64KeyMessageDigest; + } + + @Test(dataProvider = "hmacsha1") + public void testHmacSha1Base64(byte[] key, String message, String base64Digest) + throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException { + String b64 = encryptionService.hmacSha1Base64(message, key); + assertEquals(b64, base64Digest); + } + + @Test(dataProvider = "eTag") + public void testMD5Digest(String message, String base64Digest) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { + String b64 = encryptionService.md5Hex(message.getBytes()); + assertEquals(base64Digest, b64); + } + + byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 }; + String hex = "0001020408102040"; + + public void testHexStringEncode() throws UnsupportedEncodingException { + assertEquals(encryptionService.toHexString(bytes), hex); + } + + public void testHexStringDecode() throws UnsupportedEncodingException { + assertEquals(encryptionService.fromHexString(hex), bytes); + } + + public void testHexStringDecodeOx() throws UnsupportedEncodingException { + assertEquals(encryptionService.fromHexString("0x" + hex), bytes); + } +} diff --git a/core/src/test/java/org/jclouds/util/HttpUtilsTest.java b/core/src/test/java/org/jclouds/util/HttpUtilsTest.java index be449d7dc9..0c1e32403c 100644 --- a/core/src/test/java/org/jclouds/util/HttpUtilsTest.java +++ b/core/src/test/java/org/jclouds/util/HttpUtilsTest.java @@ -25,20 +25,10 @@ package org.jclouds.util; import static org.testng.Assert.assertEquals; -import java.io.UnsupportedEncodingException; import java.net.URI; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletionService; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorCompletionService; -import org.bouncycastle.util.encoders.Base64; import org.jclouds.PerformanceTest; import org.jclouds.http.HttpUtils; -import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** @@ -101,92 +91,4 @@ public class HttpUtilsTest extends PerformanceTest { .create("https://jclouds.blob.core.windows.net/jclouds-getpath/write-tests/file1%25.txt")); } - - @Test(dataProvider = "hmacsha1") - void testBouncyCastleDigestSerialResponseTime(byte[] key, String message, String base64Digest) - throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException { - for (int i = 0; i < 10000; i++) - testBouncyCastleHmacSha1Base64(key, message, base64Digest); - } - - @Test(dataProvider = "hmacsha1") - void testBouncyCastleDigestParallelResponseTime(final byte[] key, final String message, - final String base64Digest) throws NoSuchProviderException, NoSuchAlgorithmException, - InvalidKeyException, InterruptedException, ExecutionException { - CompletionService completer = new ExecutorCompletionService(exec); - for (int i = 0; i < 10000; i++) - completer.submit(new Callable() { - public Boolean call() throws Exception { - testBouncyCastleHmacSha1Base64(key, message, base64Digest); - return true; - } - }); - for (int i = 0; i < 10000; i++) - assert completer.take().get(); - } - - @DataProvider(name = "eTag") - public Object[][] createMD5Data() { - return base64MD5MessageDigest; - } - - public final static Object[][] base64MD5MessageDigest = { - { "apple", "1f3870be274f6c49b3e31a0c6728957f" }, - { "bear", "893b56e3cfe153fb770a120b83bac20c" }, - { "candy", "c48ba993d35c3abe0380f91738fe2a34" }, - { "dogma", "95eb470e4faee302e9cd3063b1923dab" }, - { "emma", "00a809937eddc44521da9521269e75c6" } }; - - public final static Object[][] base64KeyMessageDigest = { - { Base64.decode("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", - "thcxhlUFcmTii8C2+zeMjvFGvgA=" }, - { Base64.decode("SmVmZQ=="), "what do ya want for nothing?", - "7/zfauXrL6LSdBbV8YTfnCWafHk=" }, - { Base64.decode("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", - "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" }, - { - Base64 - .decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="), - "Test Using Larger Than Block-Size Key - Hash Key First", - "qkrl4VJy0A6VcFY3zoo7Ve1AIRI=" }, - { - Base64 - .decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="), - "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", - "6OmdD0UjfXhta7qnllx4CLv/GpE=" } }; - - @DataProvider(name = "hmacsha1") - public Object[][] createData1() { - return base64KeyMessageDigest; - } - - @Test(dataProvider = "hmacsha1") - public void testBouncyCastleHmacSha1Base64(byte[] key, String message, String base64Digest) - throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException { - String b64 = HttpUtils.hmacSha1Base64(message, key); - assertEquals(b64, base64Digest); - } - - @Test(dataProvider = "eTag") - public void testBouncyCastleMD5Digest(String message, String base64Digest) - throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, - UnsupportedEncodingException { - String b64 = HttpUtils.md5Hex(message.getBytes()); - assertEquals(base64Digest, b64); - } - - byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 }; - String hex = "0001020408102040"; - - public void testHexStringEncode() throws UnsupportedEncodingException { - assertEquals(HttpUtils.toHexString(bytes), hex); - } - - public void testHexStringDecode() throws UnsupportedEncodingException { - assertEquals(HttpUtils.fromHexString(hex), bytes); - } - - public void testHexStringDecodeOx() throws UnsupportedEncodingException { - assertEquals(HttpUtils.fromHexString("0x" + hex), bytes); - } } diff --git a/extensions/bouncycastle/pom.xml b/extensions/bouncycastle/pom.xml new file mode 100644 index 0000000000..0fc1f9a26c --- /dev/null +++ b/extensions/bouncycastle/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + org.jclouds + jclouds-extensions-project + 1.0-SNAPSHOT + + jclouds-bouncycastle + jclouds bouncycastle EncryptionService Module + jclouds bouncycastle EncryptionService Module + + + + org.bouncycastle + bcprov-jdk15 + 1.44 + + + diff --git a/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/BouncyCastleEncryptionService.java b/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/BouncyCastleEncryptionService.java new file mode 100644 index 0000000000..88ee2af3bd --- /dev/null +++ b/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/BouncyCastleEncryptionService.java @@ -0,0 +1,120 @@ +package org.jclouds.util.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Base64; +import org.jclouds.util.Utils; + +/** + * + * @author Adrian Cole + */ +public class BouncyCastleEncryptionService extends BaseEncryptionService { + public String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException { + Digest digest = new SHA256Digest(); + return hmacBase64(toEncode, key, digest); + } + + private String hmacBase64(String toEncode, byte[] key, Digest digest) { + byte[] resBuf = hmac(toEncode, key, digest); + return toBase64String(resBuf); + } + + public byte[] hmac(String toEncode, byte[] key, Digest digest) { + HMac hmac = new HMac(digest); + byte[] resBuf = new byte[hmac.getMacSize()]; + byte[] plainBytes = Utils.encodeString(toEncode); + byte[] keyBytes = key; + hmac.init(new KeyParameter(keyBytes)); + hmac.update(plainBytes, 0, plainBytes.length); + hmac.doFinal(resBuf, 0); + return resBuf; + } + + public String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException { + Digest digest = new SHA1Digest(); + return hmacBase64(toEncode, key, digest); + } + + public byte[] md5(byte[] plainBytes) { + MD5Digest eTag = new MD5Digest(); + byte[] resBuf = new byte[eTag.getDigestSize()]; + eTag.update(plainBytes, 0, plainBytes.length); + eTag.doFinal(resBuf, 0); + return resBuf; + } + + public byte[] md5(File toEncode) { + MD5Digest eTag = new MD5Digest(); + byte[] resBuf = new byte[eTag.getDigestSize()]; + byte[] buffer = new byte[1024]; + int numRead = -1; + InputStream i = null; + try { + i = new FileInputStream(toEncode); + do { + numRead = i.read(buffer); + if (numRead > 0) { + eTag.update(buffer, 0, numRead); + } + } while (numRead != -1); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(i); + } + eTag.doFinal(resBuf, 0); + return resBuf; + } + + public String toBase64String(byte[] resBuf) { + return new String(Base64.encode(resBuf)); + } + + public MD5InputStreamResult generateMD5Result(InputStream toEncode) { + MD5Digest eTag = new MD5Digest(); + byte[] resBuf = new byte[eTag.getDigestSize()]; + byte[] buffer = new byte[1024]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + long length = 0; + int numRead = -1; + try { + do { + numRead = toEncode.read(buffer); + if (numRead > 0) { + length += numRead; + eTag.update(buffer, 0, numRead); + out.write(buffer, 0, numRead); + } + } while (numRead != -1); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(out); + IOUtils.closeQuietly(toEncode); + } + eTag.doFinal(resBuf, 0); + return new MD5InputStreamResult(out.toByteArray(), resBuf, length); + } + + public byte[] fromBase64String(String encoded) { + return Base64.decode(encoded); + } + +} diff --git a/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/EncryptionService.java b/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/EncryptionService.java new file mode 100644 index 0000000000..b346976d44 --- /dev/null +++ b/extensions/bouncycastle/src/main/java/org/jclouds/util/internal/EncryptionService.java @@ -0,0 +1,90 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.jclouds.util.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import org.bouncycastle.crypto.Digest; + +/** + * + * @author Adrian Cole + */ +public interface EncryptionService { + + String toHexString(byte[] raw); + + byte[] fromHexString(String hex); + + String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException; + + byte[] hmac(String toEncode, byte[] key, Digest digest); + + String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException, + NoSuchProviderException, InvalidKeyException; + + String md5Hex(byte[] toEncode) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, UnsupportedEncodingException; + + String md5Base64(byte[] toEncode) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException; + + byte[] md5(byte[] plainBytes); + + byte[] md5(File toEncode); + + String toBase64String(byte[] resBuf); + + Long calculateSize(Object data); + + byte[] md5(Object data); + + MD5InputStreamResult generateMD5Result(InputStream toEncode); + + public static class MD5InputStreamResult { + public final byte[] data; + public final byte[] md5; + public final long length; + + MD5InputStreamResult(byte[] data, byte[] eTag, long length) { + this.data = checkNotNull(data, "data"); + this.md5 = checkNotNull(eTag, "eTag"); + checkArgument(length >= 0, "length cannot me negative"); + this.length = length; + } + + } + + byte[] fromBase64String(String encoded); + +} \ No newline at end of file diff --git a/extensions/bouncycastle/src/test/java/org/jclouds/util/internal/BouncyCastleEncryptionServiceTest.java b/extensions/bouncycastle/src/test/java/org/jclouds/util/internal/BouncyCastleEncryptionServiceTest.java new file mode 100644 index 0000000000..e3ba61bf7e --- /dev/null +++ b/extensions/bouncycastle/src/test/java/org/jclouds/util/internal/BouncyCastleEncryptionServiceTest.java @@ -0,0 +1,23 @@ +package org.jclouds.util.internal; + +import org.jclouds.util.EncryptionServiceTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * This tests the performance of Digest commands. + * + * @author Adrian Cole + */ +@Test(groups = "performance", sequential = true, testName = "jclouds.BouncyCastleEncryptionServiceTest") +public class BouncyCastleEncryptionServiceTest extends EncryptionServiceTest { + + @BeforeTest + protected void createEncryptionService() { + Injector i = Guice.createInjector(); + encryptionService = i.getInstance(BouncyCastleEncryptionService.class); + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index 00d4e07df9..e99926fa68 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -40,6 +40,7 @@ gae httpnio joda + bouncycastle log4j ssh diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSRestClientModule.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSRestClientModule.java index e8dbd9727c..bb8fb97420 100755 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSRestClientModule.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSRestClientModule.java @@ -54,6 +54,7 @@ import org.jclouds.mezeo.pcs2.handlers.PCSClientErrorRetryHandler; import org.jclouds.mezeo.pcs2.reference.PCSConstants; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestClientFactory; +import org.jclouds.util.EncryptionService; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -78,9 +79,9 @@ public class PCSRestClientModule extends AbstractModule { @Singleton public BasicAuthentication provideBasicAuthentication( @Named(PCSConstants.PROPERTY_PCS2_USER) String user, - @Named(PCSConstants.PROPERTY_PCS2_PASSWORD) String password) - throws UnsupportedEncodingException { - return new BasicAuthentication(user, password); + @Named(PCSConstants.PROPERTY_PCS2_PASSWORD) String password, + EncryptionService encryptionService) throws UnsupportedEncodingException { + return new BasicAuthentication(user, password, encryptionService); } @Provides diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSClientTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSClientTest.java index 213ca98028..8b88fbf628 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSClientTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSClientTest.java @@ -59,6 +59,7 @@ import org.jclouds.mezeo.pcs2.xml.FileHandler; import org.jclouds.rest.RestClientTest; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.util.EncryptionService; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -303,9 +304,9 @@ public class PCSClientTest extends RestClientTest { @SuppressWarnings("unused") @Provides @Singleton - public BasicAuthentication provideBasicAuthentication() + public BasicAuthentication provideBasicAuthentication(EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication("foo", "bar"); + return new BasicAuthentication("foo", "bar", encryptionService); } }; diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudLiveTest.java index 9ed0cd646a..a6f3311cbd 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudLiveTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudLiveTest.java @@ -41,6 +41,7 @@ import org.jclouds.logging.Logger.LoggerFactory; import org.jclouds.mezeo.pcs2.PCSCloud.Response; import org.jclouds.rest.RestClientFactory; import org.jclouds.rest.config.RestModule; +import org.jclouds.util.EncryptionService; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -94,9 +95,9 @@ public class PCSCloudLiveTest { @SuppressWarnings("unused") @Provides @Singleton - public BasicAuthentication provideBasicAuthentication() + public BasicAuthentication provideBasicAuthentication(EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication(user, password); + return new BasicAuthentication(user, password, encryptionService); } @SuppressWarnings("unused") diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudTest.java index 66b65839e0..cb37cc4daa 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSCloudTest.java @@ -42,6 +42,7 @@ import org.jclouds.mezeo.pcs2.xml.CloudXlinkHandler; import org.jclouds.rest.config.RestModule; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.util.EncryptionService; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -66,7 +67,8 @@ public class PCSCloudTest { assertEquals(httpMethod.getRequestLine(), "GET http://localhost:8080/ HTTP/1.1"); assertEquals(httpMethod.getHeaders().size(), 0); assertEquals(processor.createResponseParser(method, httpMethod).getClass(), ParseSax.class); - assertEquals(RestAnnotationProcessor.getSaxResponseParserClassOrNull(method), CloudXlinkHandler.class); + assertEquals(RestAnnotationProcessor.getSaxResponseParserClassOrNull(method), + CloudXlinkHandler.class); assertEquals(httpMethod.getFilters().size(), 1); assertEquals(httpMethod.getFilters().get(0).getClass(), BasicAuthentication.class); assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); @@ -92,9 +94,9 @@ public class PCSCloudTest { @SuppressWarnings("unused") @Provides @Singleton - public BasicAuthentication provideBasicAuthentication() + public BasicAuthentication provideBasicAuthentication(EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication("foo", "bar"); + return new BasicAuthentication("foo", "bar", encryptionService); } }, new RestModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), new JavaUrlHttpCommandExecutorServiceModule()); diff --git a/nirvanix/sdn/core/src/test/java/org/jclouds/nirvanix/sdn/SDNClientLiveTest.java b/nirvanix/sdn/core/src/test/java/org/jclouds/nirvanix/sdn/SDNClientLiveTest.java index 1387d172f0..c88e798479 100644 --- a/nirvanix/sdn/core/src/test/java/org/jclouds/nirvanix/sdn/SDNClientLiveTest.java +++ b/nirvanix/sdn/core/src/test/java/org/jclouds/nirvanix/sdn/SDNClientLiveTest.java @@ -34,9 +34,9 @@ import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; -import org.jclouds.http.HttpUtils; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.nirvanix.sdn.domain.UploadInfo; +import org.jclouds.util.internal.Base64; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; @@ -85,7 +85,7 @@ public class SDNClientLiveTest { connection.upload(uploadInfo.getHost(), uploadInfo.getToken(), containerName, blob); Map metadata = connection.getMetadata(containerName + "/test.txt"); - assertEquals(metadata.get("MD5"), HttpUtils.toBase64String(md5)); + assertEquals(metadata.get("MD5"), Base64.encodeBytes(md5)); String content = connection.getFile(containerName + "/test.txt"); assertEquals(content, "value"); @@ -94,7 +94,7 @@ public class SDNClientLiveTest { connection.setMetadata(containerName + "/test.txt", metadata); metadata = connection.getMetadata(containerName + "/test.txt"); - assertEquals(metadata.get("MD5"), HttpUtils.toBase64String(md5)); + assertEquals(metadata.get("MD5"), Base64.encodeBytes(md5)); } } diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/binders/BindCFObjectToEntity.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/binders/BindCFObjectToEntity.java index a1d62f7307..d009d36187 100644 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/binders/BindCFObjectToEntity.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/binders/BindCFObjectToEntity.java @@ -31,19 +31,22 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.blobstore.binders.BindBlobToEntityAndUserMetadataToHeadersWithPrefix; import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpUtils; import org.jclouds.rackspace.cloudfiles.blobstore.functions.ObjectToBlob; import org.jclouds.rackspace.cloudfiles.domain.CFObject; import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants; +import org.jclouds.util.EncryptionService; public class BindCFObjectToEntity extends BindBlobToEntityAndUserMetadataToHeadersWithPrefix { private final ObjectToBlob object2Blob; + private final EncryptionService encryptionService; @Inject public BindCFObjectToEntity(ObjectToBlob object2Blob, - @Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String prefix) { - super(prefix); + @Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String prefix, + EncryptionService encryptionService) { + super(prefix, encryptionService); this.object2Blob = object2Blob; + this.encryptionService = encryptionService; } public void bindToRequest(HttpRequest request, Object entity) { @@ -60,7 +63,7 @@ public class BindCFObjectToEntity extends BindBlobToEntityAndUserMetadataToHeade super.bindToRequest(request, object2Blob.apply(object)); if (object.getInfo().getHash() != null) { request.getHeaders().put(HttpHeaders.ETAG, - HttpUtils.toHexString(object.getInfo().getHash())); + encryptionService.toHexString(object.getInfo().getHash())); request.getHeaders().removeAll("Content-MD5"); } diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/blobstore/functions/ObjectToBlobMetadata.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/blobstore/functions/ObjectToBlobMetadata.java index b0df505f9a..5324e7dae3 100644 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/blobstore/functions/ObjectToBlobMetadata.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/blobstore/functions/ObjectToBlobMetadata.java @@ -30,9 +30,9 @@ import org.jclouds.blobstore.domain.MutableBlobMetadata; import org.jclouds.blobstore.domain.ResourceType; import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl; import org.jclouds.blobstore.strategy.IsDirectoryStrategy; -import org.jclouds.http.HttpUtils; import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata; import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo; +import org.jclouds.util.EncryptionService; import com.google.common.base.Function; @@ -42,10 +42,13 @@ import com.google.common.base.Function; @Singleton public class ObjectToBlobMetadata implements Function { private final IsDirectoryStrategy isDirectoryStrategy; + private final EncryptionService encryptionService; @Inject - public ObjectToBlobMetadata(IsDirectoryStrategy isDirectoryStrategy) { + public ObjectToBlobMetadata(IsDirectoryStrategy isDirectoryStrategy, + EncryptionService encryptionService) { this.isDirectoryStrategy = isDirectoryStrategy; + this.encryptionService = encryptionService; } public MutableBlobMetadata apply(ObjectInfo from) { @@ -54,7 +57,7 @@ public class ObjectToBlobMetadata implements Function { +public class ResourceToObjectInfo implements + Function { + private final EncryptionService encryptionService; + + @Inject + public ResourceToObjectInfo(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + public MutableObjectInfoWithMetadata apply(ResourceMetadata base) { MutableObjectInfoWithMetadata to = new MutableObjectInfoWithMetadataImpl(); - if (base.getType() == ResourceType.BLOB){ - to.setContentType(((BlobMetadata)base).getContentType()); - to.setHash(((BlobMetadata)base).getContentMD5()); - } else if (base.getType() == ResourceType.RELATIVE_PATH){ + if (base.getType() == ResourceType.BLOB) { + to.setContentType(((BlobMetadata) base).getContentType()); + to.setHash(((BlobMetadata) base).getContentMD5()); + } else if (base.getType() == ResourceType.RELATIVE_PATH) { to.setContentType("application/directory"); } if (base.getETag() != null && to.getHash() == null) - to.setHash(HttpUtils.fromHexString(base.getETag())); + to.setHash(encryptionService.fromHexString(base.getETag())); to.setName(base.getName()); to.setLastModified(base.getLastModified()); if (base.getSize() != null) diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoFromHeaders.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoFromHeaders.java index 4c82ff0c35..9d425d93f7 100644 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoFromHeaders.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoFromHeaders.java @@ -28,11 +28,11 @@ import javax.inject.Inject; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders; import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpUtils; import org.jclouds.rackspace.cloudfiles.blobstore.functions.ResourceToObjectInfo; import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.util.EncryptionService; import com.google.common.base.Function; @@ -45,12 +45,14 @@ public class ParseObjectInfoFromHeaders implements Function, InvocationContext { private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser; private final ResourceToObjectInfo blobToObjectInfo; + private final EncryptionService encryptionService; @Inject public ParseObjectInfoFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser, - ResourceToObjectInfo blobToObjectInfo) { + ResourceToObjectInfo blobToObjectInfo, EncryptionService encryptionService) { this.blobMetadataParser = blobMetadataParser; this.blobToObjectInfo = blobToObjectInfo; + this.encryptionService = encryptionService; } /** @@ -61,7 +63,7 @@ public class ParseObjectInfoFromHeaders implements MutableObjectInfoWithMetadata to = blobToObjectInfo.apply(base); String eTagHeader = from.getFirstHeaderOrNull("Etag"); if (eTagHeader != null) { - to.setHash(HttpUtils.fromHexString(eTagHeader.replaceAll("\"", ""))); + to.setHash(encryptionService.fromHexString(eTagHeader.replaceAll("\"", ""))); } return to; } diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoListFromJsonResponse.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoListFromJsonResponse.java index 98d7ba7e7d..e6b00c01c5 100644 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoListFromJsonResponse.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectInfoListFromJsonResponse.java @@ -37,12 +37,12 @@ import javax.inject.Inject; import org.jclouds.blobstore.domain.ListContainerResponse; import org.jclouds.blobstore.domain.internal.ListContainerResponseImpl; -import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseJson; import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo; import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.util.EncryptionService; import com.google.common.base.Function; import com.google.common.collect.Iterables; @@ -59,10 +59,12 @@ public class ParseObjectInfoListFromJsonResponse extends ParseJson> implements InvocationContext { private GeneratedHttpRequest request; + private static EncryptionService encryptionService; @Inject - public ParseObjectInfoListFromJsonResponse(Gson gson) { + public ParseObjectInfoListFromJsonResponse(Gson gson, EncryptionService encryptionService) { super(gson); + ParseObjectInfoListFromJsonResponse.encryptionService = encryptionService; } public static class ObjectInfoImpl implements ObjectInfo { @@ -85,7 +87,7 @@ public class ParseObjectInfoListFromJsonResponse extends } public byte[] getHash() { - return HttpUtils.fromHexString(hash); + return encryptionService.fromHexString(hash); } public Date getLastModified() { diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/options/CreateServerOptions.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/options/CreateServerOptions.java index a0a4b11c77..b48d91ed92 100644 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/options/CreateServerOptions.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/options/CreateServerOptions.java @@ -33,9 +33,9 @@ import java.util.Map; import java.util.Map.Entry; import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpUtils; import org.jclouds.rackspace.cloudservers.domain.Addresses; import org.jclouds.rest.binders.BindToJsonEntity; +import org.jclouds.util.internal.Base64; import com.google.common.collect.ImmutableMap; import com.google.inject.internal.Lists; @@ -54,7 +54,7 @@ public class CreateServerOptions extends BindToJsonEntity { public File(String path, byte[] contents) { this.path = checkNotNull(path, "path"); - this.contents = HttpUtils.toBase64String(checkNotNull(contents, "contents")); + this.contents = Base64.encodeBytes(checkNotNull(contents, "contents")); checkArgument(path.getBytes().length < 255, String.format( "maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path .getBytes().length)); diff --git a/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java b/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java index 418f6c516f..a107b75b13 100644 --- a/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java +++ b/rackspace/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesClientLiveTest.java @@ -44,7 +44,6 @@ import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.domain.ListResponse; import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; import org.jclouds.http.options.GetOptions; import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata; import org.jclouds.rackspace.cloudfiles.domain.CFObject; @@ -54,6 +53,8 @@ import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata; import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo; import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions; import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions; +import org.jclouds.util.EncryptionService; +import org.jclouds.util.internal.JCEEncryptionService; import org.testng.annotations.Test; import com.google.common.base.Predicate; @@ -68,6 +69,7 @@ import com.google.common.collect.Maps; @Test(groups = "live", testName = "cloudfiles.CloudFilesClientLiveTest") public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest { + private static final EncryptionService encryptionService = new JCEEncryptionService(); /** * this method overrides containerName to ensure it isn't found @@ -170,7 +172,7 @@ public class CloudFilesClientLiveTest extends assertTrue(context.getApi().disableCDN(containerNameWithCDN)); cdnMetadata = context.getApi().getCDNMetadata(containerNameWithCDN); - assertEquals(cdnMetadata.isCDNEnabled(), false); + assertEquals(cdnMetadata.isCDNEnabled(), false); } finally { recycleContainer(containerNameWithCDN); recycleContainer(containerNameWithoutCDN); @@ -307,7 +309,8 @@ public class CloudFilesClientLiveTest extends CFObject object = newCFObject(data, key); byte[] md5 = object.getInfo().getHash(); String newEtag = context.getApi().putObject(containerName, object); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo().getHash())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(object + .getInfo().getHash())); // Test HEAD of missing object try { @@ -322,8 +325,9 @@ public class CloudFilesClientLiveTest extends // TODO assertEquals(metadata.getName(), object.getMetadata().getName()); assertEquals(metadata.getBytes(), new Long(data.length())); assertEquals(metadata.getContentType(), "text/plain"); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo().getHash())); - assertEquals(metadata.getHash(), HttpUtils.fromHexString(newEtag)); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(object + .getInfo().getHash())); + assertEquals(metadata.getHash(), encryptionService.fromHexString(newEtag)); assertEquals(metadata.getMetadata().entrySet().size(), 1); assertEquals(metadata.getMetadata().get("metadata"), "metadata-value"); @@ -346,9 +350,9 @@ public class CloudFilesClientLiveTest extends // TODO assertEquals(getBlob.getName(), object.getMetadata().getName()); assertEquals(getBlob.getContentLength(), new Long(data.length())); assertEquals(getBlob.getInfo().getContentType(), "text/plain"); - assertEquals(HttpUtils.toHexString(md5), HttpUtils - .toHexString(getBlob.getInfo().getHash())); - assertEquals(HttpUtils.fromHexString(newEtag), getBlob.getInfo().getHash()); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(getBlob + .getInfo().getHash())); + assertEquals(encryptionService.fromHexString(newEtag), getBlob.getInfo().getHash()); assertEquals(getBlob.getInfo().getMetadata().entrySet().size(), 2); assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-1"), "value-1"); assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-2"), "value-2"); @@ -356,7 +360,7 @@ public class CloudFilesClientLiveTest extends // Test PUT with invalid ETag (as if object's data was corrupted in transit) String correctEtag = newEtag; String incorrectEtag = "0" + correctEtag.substring(1); - object.getInfo().setHash(HttpUtils.fromHexString(incorrectEtag)); + object.getInfo().setHash(encryptionService.fromHexString(incorrectEtag)); try { context.getApi().putObject(containerName, object); } catch (HttpResponseException e) { @@ -369,8 +373,8 @@ public class CloudFilesClientLiveTest extends blob.getInfo().setName("chunked-object"); blob.setData(bais); newEtag = context.getApi().putObject(containerName, blob); - assertEquals(HttpUtils.toHexString(md5), HttpUtils - .toHexString(getBlob.getInfo().getHash())); + assertEquals(encryptionService.toHexString(md5), encryptionService.toHexString(getBlob + .getInfo().getHash())); // Test GET with options // Non-matching ETag @@ -384,7 +388,7 @@ public class CloudFilesClientLiveTest extends // Matching ETag getBlob = context.getApi().getObject(containerName, object.getInfo().getName(), GetOptions.Builder.ifETagMatches(newEtag)); - assertEquals(getBlob.getInfo().getHash(), HttpUtils.fromHexString(newEtag)); + assertEquals(getBlob.getInfo().getHash(), encryptionService.fromHexString(newEtag)); getBlob = context.getApi().getObject(containerName, object.getInfo().getName(), GetOptions.Builder.startAt(8)); assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8)); diff --git a/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java b/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java index 54a2ae3d37..14abc1af96 100644 --- a/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java +++ b/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingAsyncClientTest.java @@ -23,45 +23,50 @@ */ package org.jclouds.rimuhosting.miro; -import com.google.inject.AbstractModule; -import com.google.inject.Module; -import com.google.inject.Provides; -import com.google.inject.TypeLiteral; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; + +import javax.inject.Singleton; + import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.logging.Logger; import org.jclouds.logging.Logger.LoggerFactory; import org.jclouds.rest.RestClientTest; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; -import static org.testng.Assert.assertEquals; +import org.jclouds.util.EncryptionService; import org.testng.annotations.Test; -import javax.inject.Singleton; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; /** * Tests annotation parsing of {@code RimuHostingAsyncClient} - * + * * @author Ivan Meredith */ @Test(groups = "unit", testName = "rimuhosting.RimuHostingAsyncClientTest") public class RimuHostingAsyncClientTest extends RestClientTest { public void testGetMyMentions() throws SecurityException, NoSuchMethodException, IOException { - /* Method method = RimuHostingAsyncClient.class.getMethod("TODO: insert test method name"); - GeneratedHttpRequest httpMethod = processor.createRequest(method); - - assertRequestLineEquals(httpMethod, "TODO: insert expected request"); - assertHeadersEqual(httpMethod, ""); - assertEntityEquals(httpMethod, null); - - assertResponseParserClassEquals(method, httpMethod, ParseStatusesFromJsonResponse.class); - assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); - - checkFilters(httpMethod);*/ + /* + * Method method = RimuHostingAsyncClient.class.getMethod("TODO: insert test method name"); + * GeneratedHttpRequest httpMethod = processor.createRequest(method); + * + * assertRequestLineEquals(httpMethod, "TODO: insert expected request"); + * assertHeadersEqual(httpMethod, ""); assertEntityEquals(httpMethod, null); + * + * assertResponseParserClassEquals(method, httpMethod, ParseStatusesFromJsonResponse.class); + * assertSaxResponseParserClassEquals(method, null); assertExceptionParserClassEquals(method, + * null); + * + * checkFilters(httpMethod); + */ } @Override @@ -82,7 +87,7 @@ public class RimuHostingAsyncClientTest extends RestClientTest { @SuppressWarnings("unused") @Provides @Singleton - public BasicAuthentication provideBasicAuthentication() + public BasicAuthentication provideBasicAuthentication(EncryptionService encryptionService) throws UnsupportedEncodingException { - return new BasicAuthentication("foo", "bar"); + return new BasicAuthentication("foo", "bar", encryptionService); } }; diff --git a/vcloud/core/src/main/java/org/jclouds/vcloud/config/VCloudDiscoveryRestClientModule.java b/vcloud/core/src/main/java/org/jclouds/vcloud/config/VCloudDiscoveryRestClientModule.java index d38d057adc..3aeab60d30 100755 --- a/vcloud/core/src/main/java/org/jclouds/vcloud/config/VCloudDiscoveryRestClientModule.java +++ b/vcloud/core/src/main/java/org/jclouds/vcloud/config/VCloudDiscoveryRestClientModule.java @@ -43,6 +43,7 @@ import org.jclouds.http.RequiresHttp; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestClientFactory; +import org.jclouds.util.EncryptionService; import org.jclouds.util.Utils; import org.jclouds.vcloud.VCloudDiscovery; import org.jclouds.vcloud.VCloudLogin; @@ -147,8 +148,9 @@ public class VCloudDiscoveryRestClientModule extends AbstractModule { @Provides @Singleton public BasicAuthentication provideBasicAuthentication(@Named(PROPERTY_VCLOUD_USER) String user, - @Named(PROPERTY_VCLOUD_KEY) String key) throws UnsupportedEncodingException { - return new BasicAuthentication(user, key); + @Named(PROPERTY_VCLOUD_KEY) String key, EncryptionService encryptionService) + throws UnsupportedEncodingException { + return new BasicAuthentication(user, key, encryptionService); } } diff --git a/vcloud/core/src/test/java/org/jclouds/vcloud/VCloudLoginTest.java b/vcloud/core/src/test/java/org/jclouds/vcloud/VCloudLoginTest.java index 42cefe26db..12ee37f72d 100755 --- a/vcloud/core/src/test/java/org/jclouds/vcloud/VCloudLoginTest.java +++ b/vcloud/core/src/test/java/org/jclouds/vcloud/VCloudLoginTest.java @@ -38,6 +38,7 @@ import org.jclouds.logging.Logger.LoggerFactory; import org.jclouds.rest.RestClientTest; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.util.internal.JCEEncryptionService; import org.jclouds.vcloud.functions.ParseLoginResponseFromHeaders; import org.testng.annotations.Test; @@ -58,7 +59,8 @@ public class VCloudLoginTest extends RestClientTest { GeneratedHttpRequest httpMethod = processor.createRequest(method); assertEquals(httpMethod.getRequestLine(), "POST http://localhost:8080/login HTTP/1.1"); - assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": application/vnd.vmware.vcloud.organizationList+xml\n"); + assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + + ": application/vnd.vmware.vcloud.organizationList+xml\n"); assertEntityEquals(httpMethod, null); assertResponseParserClassEquals(method, httpMethod, ParseLoginResponseFromHeaders.class); @@ -88,7 +90,8 @@ public class VCloudLoginTest extends RestClientTest { bind(URI.class).annotatedWith(org.jclouds.vcloud.endpoints.VCloudLogin.class) .toInstance(URI.create("http://localhost:8080/login")); try { - bind(BasicAuthentication.class).toInstance(new BasicAuthentication("user", "pass")); + bind(BasicAuthentication.class).toInstance( + new BasicAuthentication("user", "pass", new JCEEncryptionService())); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }