/**
   * Builds an instruction object from the contents of an instruction file.
   *
   * @param instructionFile A non-null instruction file retrieved from S3 that contains encryption
   *     information
   * @param materialsProvider The non-null encryption materials provider to be used to encrypt and
   *     decrypt data.
   * @param cryptoProvider The crypto provider whose encryption implementation will be used to
   *     encrypt and decrypt data. Null is ok and uses the preferred provider from
   *     Security.getProviders().
   * @return A non-null instruction object containing encryption information
   * @deprecated no longer used and will be removed in the future
   */
  @Deprecated
  public static EncryptionInstruction buildInstructionFromInstructionFile(
      S3Object instructionFile,
      EncryptionMaterialsProvider materialsProvider,
      Provider cryptoProvider) {
    JSONObject instructionJSON = parseJSONInstruction(instructionFile);
    try {
      // Get fields from instruction object
      String encryptedSymmetricKeyB64 = instructionJSON.getString(Headers.CRYPTO_KEY);
      String ivB64 = instructionJSON.getString(Headers.CRYPTO_IV);
      String materialsDescriptionString =
          instructionJSON.tryGetString(Headers.MATERIALS_DESCRIPTION);
      Map<String, String> materialsDescription = convertJSONToMap(materialsDescriptionString);

      // Decode from Base 64 to standard binary bytes
      byte[] encryptedSymmetricKey = Base64.decode(encryptedSymmetricKeyB64);
      byte[] iv = Base64.decode(ivB64);

      if (encryptedSymmetricKey == null || iv == null) {
        // If necessary encryption info was not found in the instruction file, throw an exception.
        throw new AmazonClientException(
            String.format(
                "Necessary encryption info not found in the instruction file '%s' in bucket '%s'",
                instructionFile.getKey(), instructionFile.getBucketName()));
      }

      EncryptionMaterials materials =
          retrieveOriginalMaterials(materialsDescription, materialsProvider);
      // If we're unable to retrieve the original encryption materials, we can't decrypt the object,
      // so
      // throw an exception.
      if (materials == null) {
        throw new AmazonClientException(
            String.format(
                "Unable to retrieve the encryption materials that originally "
                    + "encrypted object corresponding to instruction file '%s' in bucket '%s'.",
                instructionFile.getKey(), instructionFile.getBucketName()));
      }

      // Decrypt the symmetric key and create the symmetric cipher
      SecretKey symmetricKey =
          getDecryptedSymmetricKey(encryptedSymmetricKey, materials, cryptoProvider);
      CipherFactory cipherFactory =
          new CipherFactory(symmetricKey, Cipher.DECRYPT_MODE, iv, cryptoProvider);

      return new EncryptionInstruction(
          materialsDescription, encryptedSymmetricKey, symmetricKey, cipherFactory);
    } catch (JSONException e) {
      throw new AmazonClientException(
          "Unable to parse retrieved instruction file : " + e.getMessage());
    }
  }
  /** @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse) */
  public AmazonWebServiceResponse<S3Object> handle(HttpResponse response) throws Exception {
    /*
     * TODO: It'd be nice to set the bucket name and key here, but the
     *       information isn't easy to pull out of the response/request
     *       currently.
     */
    S3Object object = new S3Object();
    AmazonWebServiceResponse<S3Object> awsResponse = parseResponseMetadata(response);
    if (response.getHeaders().get(Headers.REDIRECT_LOCATION) != null) {
      object.setRedirectLocation(response.getHeaders().get(Headers.REDIRECT_LOCATION));
    }
    ObjectMetadata metadata = object.getObjectMetadata();
    populateObjectMetadata(response, metadata);
    boolean hasServerSideCalculatedChecksum =
        !ServiceUtils.isMultipartUploadETag(metadata.getETag());
    boolean responseContainsEntireObject = response.getHeaders().get("Content-Range") == null;

    if (hasServerSideCalculatedChecksum && responseContainsEntireObject) {
      byte[] expectedChecksum = BinaryUtils.fromHex(metadata.getETag());
      object.setObjectContent(
          new S3ObjectInputStream(
              new ChecksumValidatingInputStream(
                  response.getContent(),
                  expectedChecksum,
                  object.getBucketName() + "/" + object.getKey()),
              response.getHttpRequest()));
    } else {
      object.setObjectContent(
          new S3ObjectInputStream(response.getContent(), response.getHttpRequest()));
    }

    awsResponse.setResult(object);
    return awsResponse;
  }
  /**
   * Builds an instruction object from the object metadata.
   *
   * @param object A non-null object that contains encryption information in its headers
   * @param materialsProvider The non-null encryption materials provider to be used to encrypt and
   *     decrypt data.
   * @param cryptoProvider The crypto provider whose encryption implementation will be used to
   *     encrypt and decrypt data. Null is ok and uses the preferred provider from
   *     Security.getProviders().
   * @return A non-null instruction object containing encryption information
   * @throws AmazonClientException if encryption information is missing in the metadata, or the
   *     encryption materials used to encrypt the object are not available via the materials
   *     Accessor
   * @deprecated no longer used and will be removed in the future
   */
  @Deprecated
  public static EncryptionInstruction buildInstructionFromObjectMetadata(
      S3Object object, EncryptionMaterialsProvider materialsProvider, Provider cryptoProvider) {
    ObjectMetadata metadata = object.getObjectMetadata();

    // Get encryption info from metadata.
    byte[] encryptedSymmetricKeyBytes = getCryptoBytesFromMetadata(Headers.CRYPTO_KEY, metadata);
    byte[] initVectorBytes = getCryptoBytesFromMetadata(Headers.CRYPTO_IV, metadata);
    String materialsDescriptionString =
        getStringFromMetadata(Headers.MATERIALS_DESCRIPTION, metadata);
    Map<String, String> materialsDescription = convertJSONToMap(materialsDescriptionString);

    if (encryptedSymmetricKeyBytes == null || initVectorBytes == null) {
      // If necessary encryption info was not found in the instruction file, throw an exception.
      throw new AmazonClientException(
          String.format(
              "Necessary encryption info not found in the headers of file '%s' in bucket '%s'",
              object.getKey(), object.getBucketName()));
    }

    EncryptionMaterials materials =
        retrieveOriginalMaterials(materialsDescription, materialsProvider);
    // If we're unable to retrieve the original encryption materials, we can't decrypt the object,
    // so
    // throw an exception.
    if (materials == null) {
      throw new AmazonClientException(
          String.format(
              "Unable to retrieve the encryption materials that originally "
                  + "encrypted file '%s' in bucket '%s'.",
              object.getKey(), object.getBucketName()));
    }

    // Decrypt the symmetric key and create the symmetric cipher
    SecretKey symmetricKey =
        getDecryptedSymmetricKey(encryptedSymmetricKeyBytes, materials, cryptoProvider);
    CipherFactory cipherFactory =
        new CipherFactory(symmetricKey, Cipher.DECRYPT_MODE, initVectorBytes, cryptoProvider);

    return new EncryptionInstruction(
        materialsDescription, encryptedSymmetricKeyBytes, symmetricKey, cipherFactory);
  }
示例#4
0
  /**
   * Gets an object stored in S3 and downloads it into the specified file. This method includes the
   * one-time retry mechanism after integrity check failure on the downloaded file. It will also
   * return immediately after getting null valued S3Object (when getObject request does not meet the
   * specified constraints).
   *
   * @param file The file to store the object's data in.
   * @param safeS3DownloadTask The implementation of SafeS3DownloadTask interface which allows user
   *     to get access to all the visible variables at the calling site of this method.
   */
  public static S3Object retryableDownloadS3ObjectToFile(
      File file, RetryableS3DownloadTask retryableS3DownloadTask) {
    boolean hasRetried = false;
    boolean needRetry;
    S3Object s3Object;
    do {
      needRetry = false;
      s3Object = retryableS3DownloadTask.getS3ObjectStream();
      if (s3Object == null) return null;

      try {
        ServiceUtils.downloadObjectToFile(
            s3Object, file, retryableS3DownloadTask.needIntegrityCheck());
      } catch (AmazonClientException ace) {
        // Determine whether an immediate retry is needed according to the captured
        // AmazonClientException.
        // (There are three cases when downloadObjectToFile() throws AmazonClientException:
        // 		1) SocketException or SSLProtocolException when writing to disk (e.g. when user aborts
        // the download)
        //		2) Other IOException when writing to disk
        //		3) MD5 hashes don't match
        // The current code will retry the download only when case 2) or 3) happens.
        if (ace.getCause() instanceof SocketException
            || ace.getCause() instanceof SSLProtocolException) {
          throw ace;
        } else {
          needRetry = true;
          if (hasRetried) throw ace;
          else {
            log.info(
                "Retry the download of object "
                    + s3Object.getKey()
                    + " (bucket "
                    + s3Object.getBucketName()
                    + ")",
                ace);
            hasRetried = true;
          }
        }
      } finally {
        try {
          s3Object.getObjectContent().abort();
        } catch (IOException e) {
        }
      }
    } while (needRetry);
    return s3Object;
  }
 String getBucketName() {
   return s3obj.getBucketName();
 }
  /* (non-Javadoc)
   * @see com.amazonaws.services.s3.AmazonS3#getObject(com.amazonaws.services.s3.model.GetObjectRequest)
   */
  @Override
  public S3Object getObject(GetObjectRequest getObjectRequest)
      throws AmazonClientException, AmazonServiceException {

    appendUserAgent(getObjectRequest, USER_AGENT);

    // Adjust the crypto range to retrieve all of the cipher blocks needed to contain the user's
    // desired
    // range of bytes.
    long[] desiredRange = getObjectRequest.getRange();
    long[] adjustedCryptoRange = EncryptionUtils.getAdjustedCryptoRange(desiredRange);
    if (adjustedCryptoRange != null) {
      getObjectRequest.setRange(adjustedCryptoRange[0], adjustedCryptoRange[1]);
    }

    // Get the object from S3
    S3Object retrievedObject = super.getObject(getObjectRequest);

    // If the caller has specified constraints, it's possible that super.getObject(...)
    // would return null, so we simply return null as well.
    if (retrievedObject == null) return null;

    S3Object objectToBeReturned;
    try {
      // Check if encryption info is in object metadata
      if (EncryptionUtils.isEncryptionInfoInMetadata(retrievedObject)) {
        objectToBeReturned = decryptObjectUsingMetadata(retrievedObject);
      } else {
        // Check if encrypted info is in an instruction file
        S3Object instructionFile = null;
        try {
          instructionFile = getInstructionFile(getObjectRequest);
          if (EncryptionUtils.isEncryptionInfoInInstructionFile(instructionFile)) {
            objectToBeReturned =
                decryptObjectUsingInstructionFile(retrievedObject, instructionFile);
          } else {
            // The object was not encrypted to begin with.  Return the object without decrypting it.
            log.warn(
                String.format(
                    "Unable to detect encryption information for object '%s' in bucket '%s'. "
                        + "Returning object without decryption.",
                    retrievedObject.getKey(), retrievedObject.getBucketName()));
            objectToBeReturned = retrievedObject;
          }
        } finally {
          if (instructionFile != null) {
            try {
              instructionFile.getObjectContent().close();
            } catch (Exception e) {
            }
          }
        }
      }
    } catch (AmazonClientException ace) {
      // If we're unable to set up the decryption, make sure we close the HTTP connection
      try {
        retrievedObject.getObjectContent().close();
      } catch (Exception e) {
      }
      throw ace;
    }

    // Adjust the output to the desired range of bytes.
    return EncryptionUtils.adjustOutputToDesiredRange(objectToBeReturned, desiredRange);
  }