
# 构建程序
本例提供了为文件加/解密的程序包，使用空白模板创建函数，用户可以使用示例代码学习使用。
#### 创建程序包
本例使用Java8语言实现加/解密的功能，有关函数开发的过程请参考[Java函数开发](https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0430.html)。本例不再介绍业务功能实现的代码，样例代码目录如[图1]所示。
图1样例代码目录   
![](https://support.huaweicloud.com/bestpractice-functiongraph/zh-cn_image_0000002129944446.png "点击放大")
其中FileEncryptAndDecrypt为函数执行的入口类，FileEncryptAndDecrypt类中入口函数的代码如下：
```
package com.huawei.kms;
import com.huawei.services.runtime.Context;
import com.huawei.services.runtime.entity.s3obs.S3ObsTriggerEvent;
import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.kms.v1.KmsClient;
import com.huaweicloud.sdk.kms.v1.model.*;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.ObsObject;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.security.SecureRandom;
public class FileEncryptAndDecrypt {
    private String objectKey;
    private  String inputPath;
    private String outputPath;
    public String encrypt(S3ObsTriggerEvent event, Context context){
        objectKey = event.getObjectKey();
        inputPath = "/tmp/" + objectKey;
        outputPath = "/tmp/" + objectKey + ".encrypt";
        // 初始化obs类
        obsClientHandler client = new obsClientHandler();
        client.init(context);
        client.setObjectInfo(objectKey, inputPath, outputPath);
        // 下载obs桶里面的文件
        client.downloadFile();
        // 初始化kms类
        KmsClientHandler kms = new KmsClientHandler();
        kms.init(context);
        kms.setPath(inputPath, outputPath);
        // 加密文件
        kms.encryptFile();
        // 上传
        client.uploadFile();
        return "ok";
    }
    public String decrypt(S3ObsTriggerEvent event, Context context){
        objectKey = event.getObjectKey();
        inputPath = "/tmp/" + objectKey;
        outputPath = "/tmp/" + objectKey + ".decrypt";
        // 初始化obs类
        obsClientHandler client = new obsClientHandler();
        client.init(context);
        client.setObjectInfo(objectKey, inputPath, outputPath);
        // 下载obs桶里面的文件
        client.downloadFile();
        // 初始化kms类
        KmsClientHandler kms = new KmsClientHandler();
        kms.init(context);
        kms.setPath(inputPath, outputPath);
        // 加密文件
        kms.decryptFile();
        // 上传
        client.uploadFile();
        return "ok";
    }
    static class KmsClientHandler {
        // DEW服务接口版本信息，当前固定为v1.0
        private static final String KMS_INTERFACE_VERSION = "v1.0";
        private static final String AES_KEY_BIT_LENGTH = "256";
        private static final String AES_KEY_BYTE_LENGTH = "32";
        private static final String AES_ALG = "AES/GCM/PKCS5Padding";
        private static final String AES_FLAG = "AES";
        private static final int GCM_TAG_LENGTH = 16;
        private static final int GCM_IV_LENGTH = 12;
        private String ACCESS_KEY;
        private String SECRET_ACCESS_KEY;
        private String SECURITY_TOKEN;
        private String PROJECT_ID;
        private String KMS_ENDPOINT;
        private String keyId;
        private String cipherText;
        private String inputPath;
        private String outputPath;
        private Context context;
        private KmsClient kmsClient = null;
        void init(Context context) {
            this.context = context;
        }
        void initKmsClient() {
            if (kmsClient == null) {
                ACCESS_KEY = context.getSecurityAccessKey();
                SECRET_ACCESS_KEY = context.SecuritygetSecretKey();
                SECURITY_TOKEN = context.getSecurityToken();
                PROJECT_ID = context.getProjectID();
                KMS_ENDPOINT = context.getUserData("kms_endpoint");
                keyId = context.getUserData("kms_key_id");
                cipherText = context.getUserData("cipher_text");
                final BasicCredentials auth = new BasicCredentials().withAk(ACCESS_KEY).withSk(SECRET_ACCESS_KEY).withSecurityToken(SECURITY_TOKEN).withProjectId(PROJECT_ID);
                kmsClient = kmsClient.newBuilder().withCredential(auth).withEndpoint(KMS_ENDPOINT).build();
            }
        }
        byte[] getEncryptPlainKey() {
            final CreateDatakeyRequest createDatakeyRequest = new CreateDatakeyRequest().withVersionId(KMS_INTERFACE_VERSION)
                    .withBody(new CreateDatakeyRequestBody().withKeyId(keyId).withDatakeyLength(AES_KEY_BIT_LENGTH));
            final CreateDatakeyResponse createDatakeyResponse = kmsClient.createDatakey(createDatakeyRequest);
            final String cipherText = createDatakeyResponse.getCipherText();
            return hexToBytes(createDatakeyResponse.getPlainText());
        }
        byte[] hexToBytes(String hexString) {
            final int stringLength = hexString.length();
            assert stringLength > 0;
            final byte[] result = new byte[stringLength / 2];
            int j = 0;
            for (int i = 0; i < stringLength; i += 2) {
                result[j++] = (byte) Integer.parseInt(hexString.substring(i, i + 2), 16);
            }
            return result;
        }
        public void setPath(String inputPath, String outputPath) {
            this.inputPath = inputPath;
            this.outputPath = outputPath;
        }
        public void  encryptFile() {
            final File outEncryptFile = new File(outputPath);
            final File inFile = new File(inputPath);
            final byte[] iv = new byte[GCM_IV_LENGTH];
            final SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(iv);
            doFileFinal(Cipher.ENCRYPT_MODE, inFile, outEncryptFile, getEncryptPlainKey(), iv);
        }
        byte[] getDecryptPlainKey() {
final CreateDatakeyRequest createDatakeyRequest = new CreateDatakeyRequest().withVersionId(KMS_INTERFACE_VERSION)
        .withBody(new CreateDatakeyRequestBody().withKeyId(keyId).withDatakeyLength(AES_KEY_BIT_LENGTH));
// 创建数据密钥
final CreateDatakeyResponse createDatakeyResponse = kmsClient.createDatakey(createDatakeyRequest);
            final DecryptDatakeyRequest decryptDatakeyRequest  = new DecryptDatakeyRequest().withVersionId(KMS_INTERFACE_VERSION)
                    .withBody(new DecryptDatakeyRequestBody().withKeyId(keyId).withCipherText(createDatakeyResponse.getCipherText()
).withDatakeyCipherLength(AES_KEY_BYTE_LENGTH));
            return hexToBytes(kmsClient.decryptDatakey(decryptDatakeyRequest).getDataKey());
        }
        public void  decryptFile() {
            final File outEncryptFile = new File(outputPath);
            final File inFile = new File(inputPath);
            final byte[] iv = new byte[GCM_IV_LENGTH];
            final SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(iv);
            doFileFinal(Cipher.DECRYPT_MODE, inFile, outEncryptFile, getDecryptPlainKey(), iv);
        }
        /**          //     * 对文件进行加解密          //     *          //     * @param cipherMode 加密模式，可选值为Cipher.ENCRYPT_MODE或者Cipher.DECRYPT_MODE          //     * @param infile     加解密前的文件          //     * @param outFile    加解密后的文件          //     * @param keyPlain   明文密钥          //     * @param iv         初始化向量          //     */         void doFileFinal(int cipherMode, File infile, File outFile, byte[] keyPlain, byte[] iv) {
            try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(infile.toPath()));
                 BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(outFile.toPath()))) {
                final byte[] bytIn = new byte[(int) infile.length()];
                final int fileLength = bis.read(bytIn);
                assert fileLength > 0;
                final SecretKeySpec secretKeySpec = new SecretKeySpec(keyPlain, AES_FLAG);
                final Cipher cipher = Cipher.getInstance(AES_ALG);
                final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * Byte.SIZE, iv);
                cipher.init(cipherMode, secretKeySpec, gcmParameterSpec);
                final byte[] bytOut = cipher.doFinal(bytIn);
                bos.write(bytOut);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    }
    static class obsClientHandler {
        private ObsClient obsClient = null;
        private String inputBucketName;
        private String outputBucketName;
        private String objectKey;
        private Context context;
        private String localInPath;
        private String localOutPath;
        public void init(Context context) {
            this.context = context;
        }
        void initObsclient() {
            if (obsClient == null) {
                inputBucketName = context.getUserData("input_bucket");
                outputBucketName = context.getUserData("output_bucket");
                String SECURITY_ACCESS_KEY = context.getSecurityAccessKey();
                String SECURITY_SECRET_KEY = context.getSecuritySecretKey();
                String SECURITY_TOKEN = context.getSecurityToken();
                String OBS_ENDPOINT = context.getUserData("obs_endpoint");
                obsClient = new ObsClient(SECURITY_ACCESS_KEY, SECURITY_SECRET_KEY, SECURITY_TOKEN, OBS_ENDPOINT);
            }
        }
        public  void setObjectInfo(String objectKey, String inPath, String outPath) {
            this.objectKey = objectKey;
            localInPath = inPath;
            localOutPath = outPath;
        }
        public void downloadFile() {
            initObsclient();
            try {
                ObsObject obsObject = obsClient.getObject(inputBucketName, objectKey);
                InputStream inputStream = obsObject.getObjectContent();
                byte[] b = new byte[1024];
                int len;
                FileOutputStream fileOutputStream = new FileOutputStream("/tmp/" + objectKey);
                while ((len = inputStream.read(b)) != -1) {
                    fileOutputStream.write(b);
                }
                inputStream.close();
                fileOutputStream.close();
            } catch (ObsException ex) {
                ex.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        public void uploadFile() {
            try {
                // 待上传的本地文件路径，需要指定到具体的文件名
                FileInputStream fis = new FileInputStream(new File("/tmp/" + objectKey + ".encrypt"));
                obsClient.putObject(outputBucketName, objectKey, fis);
                fis.close();
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
```
#### 创建函数
创建函数的时候，必须选择委托包含OBS和DEW访问权限的委托，否则不能使用OBS和DEW服务。
1. 登录[函数工作流控制台](https://console.huaweicloud.com/functiongraph/)，在左侧导航栏选择"函数 \> 函数列表"，进入函数列表界面。
2. 单击"创建函数"，进入创建函数流程。
3. 选择"创建空白函数"，填写函数配置信息。 
   输入基础配置信息，完成后单击"创建函数"。
   - 函数类型：事件函数。
   
   - 函数名称：输入您自定义的函数名称，此处以"fss_examples_dew"为例。
   
   - 委托名称：选择创建委托中创建的"serverless_trust"。
   
   - 运行时语言：选择"Java8"。
   
   
   
   
4. 进入fss_examples_dew函数详情页，配置如下信息。 
   1. 在"代码"页签，代码选择"上传自JAR文件"，上传样例代码编译后的jar包，上传成功后单击"确定"。
   
   2. 在"设置 \> 常规设置"页签，设置如下信息，完成后单击"保存"。
      - 内存：选择"128"
      
      - 执行超时时间：输入"3"
      
      - 函数执行入口：默认"com.huawei.kms.FileEncryptAndDecrypt.encrypt"
      
      - 所属应用：默认"default"
      
      - 描述：输入"文件加解密"
       
   
   3. 在"设置 \> 环境变量"页签，输入环境信息，完成后单击"保存"。 dew_endpoint：dew服务的endpoint地址
      dew_key_id：用户主密钥ID。
      input_bucket: 输入文件对应的obs桶。
      output_bucket: 加解密后上传的obs桶。
      obs_endpoint: obs服务对应的endpoint。
      表1环境变量 
      | 环境变量          | 说明                                                                          |
      |:---|:---|
      | dew_endpoint  | DEW服务终端节点，获取地址请参考[地区和终端节点](https://developer.huaweicloud.com/endpoint?DEW)。 |
      | dew_key_id    | 用户主密钥ID。                                                                    |
      | input_bucket  | 存放输入文件的OBS桶。                                                                |
      | output_bucket | 存放加密后上传文件的OBS桶。                                                             |
      | obs_endpoint  | OBS服务终端节点，获取地址请参考[地区和终端节点](https://developer.huaweicloud.com/endpoint?OBS)。 |
         
      
   
   
   
   
 
