Firebase Cloud Messaging + Django:如何安全地存储服务帐户的私钥?

时间:2018-04-09 13:15:09

标签: django firebase firebase-authentication firebase-cloud-messaging private-key

我刚刚开始在我的Django后端实现FCM。

我遇到的问题如下。

the docs中,系统会告诉您生成私钥JSON文件并安全地存储它。 通常我将我的密钥存储在os.env变量中。但这是不可能的,因为这是一个完整的文件,而不仅仅是一个值。同样在同一页面上,doc会告诉您如何获取请求令牌:

def _get_access_token():
  """Retrieve a valid access token that can be used to authorize requests.

  :return: Access token.
  """
  credentials = ServiceAccountCredentials.from_json_keyfile_name(
      'service-account.json', SCOPES)
  access_token_info = credentials.get_access_token()
  return access_token_info.access_token

正如您在此处所见,库需要直接访问该文件。

所以我的问题是?我该如何安全存储?我目前在heroku上托管,所以我需要在我的版本控制系统中使用它。

4 个答案:

答案 0 :(得分:3)

我们这样做的方式:

加密资源文件中的json字符串/ Store 解密密钥,存储为Env变量。

创建凭据时解密加密的字符串。

答案 1 :(得分:2)

这是基于ivanspenchev在他的帖子中提出的建议。

流程是:读取json数据 - >用密钥加密 - >用密钥解密。我没有使用他建议的加密算法,因为我不喜欢它。

但基本的想法是,你有一个课程" EncryptEngine"做两件事,加密一个字符串,并解密一个字符串。

public class EncryptEngine
{

Cipher ecipher;
Cipher dcipher;
// 8-byte Salt
byte[] salt = {
    (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
    (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
// Iteration count
int iterationCount = 19;

public EncryptEngine() {

}

/**
 *
 * @param secretKey Key used to encrypt data
 * @param plainText Text input to be encrypted
 * @return Returns encrypted text
 * @throws java.security.NoSuchAlgorithmException
 * @throws java.security.spec.InvalidKeySpecException
 * @throws javax.crypto.NoSuchPaddingException
 * @throws java.security.InvalidKeyException
 * @throws java.security.InvalidAlgorithmParameterException
 * @throws java.io.UnsupportedEncodingException
 * @throws javax.crypto.IllegalBlockSizeException
 * @throws javax.crypto.BadPaddingException
 *
 */
public String encrypt(String secretKey, String plainText)
        throws NoSuchAlgorithmException,
        InvalidKeySpecException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        UnsupportedEncodingException,
        IllegalBlockSizeException,
        BadPaddingException {
    //Key generation for enc and desc
    KeySpec keySpec = new PBEKeySpec(secretKey.toCharArray(), salt, iterationCount);
    SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
    // Prepare the parameter to the ciphers
    AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

    //Enc process
    ecipher = Cipher.getInstance(key.getAlgorithm());
    ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
    String charSet = "UTF-8";
    byte[] in = plainText.getBytes(charSet);
    byte[] out = ecipher.doFinal(in);
    String encStr = new String(Base64.getEncoder().encode(out));
    return encStr;
}

/**
 * @param secretKey Key used to decrypt data
 * @param encryptedText encrypted text input to decrypt
 * @return Returns plain text after decryption
 * @throws java.security.NoSuchAlgorithmException
 * @throws java.security.spec.InvalidKeySpecException
 * @throws javax.crypto.NoSuchPaddingException
 * @throws java.security.InvalidKeyException
 * @throws java.security.InvalidAlgorithmParameterException
 * @throws java.io.UnsupportedEncodingException
 * @throws javax.crypto.IllegalBlockSizeException
 * @throws javax.crypto.BadPaddingException
 */
public String decrypt(String secretKey, String encryptedText)
        throws NoSuchAlgorithmException,
        InvalidKeySpecException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        UnsupportedEncodingException,
        IllegalBlockSizeException,
        BadPaddingException,
        IOException {
    //Key generation for enc and desc
    KeySpec keySpec = new PBEKeySpec(secretKey.toCharArray(), salt, iterationCount);
    SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
    // Prepare the parameter to the ciphers
    AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
    //Decryption process; same key will be used for decr
    dcipher = Cipher.getInstance(key.getAlgorithm());
    dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
    byte[] enc = Base64.getDecoder().decode(encryptedText);
    byte[] utf8 = dcipher.doFinal(enc);
    String charSet = "UTF-8";
    String plainStr = new String(utf8, charSet);
    return plainStr;
}    
public static void main(String[] args) throws Exception {
    EncryptEngine cryptoUtil=new EncryptEngine();
    String key="ezeon8547";   
    JsonFactory f = new JsonFactory();
    JsonParser jp = 
    f.createJsonParser(/MyDocuments/Repo/Myproject/service-account.json);
    String plain;
  while (jp.nextToken() == JsonToken.START_OBJECT)) {
         plain += jp.toString();
       }
    String enc=cryptoUtil.encrypt(key, plain);
    System.out.println("Original text: "+plain);
    System.out.println("Encrypted text: "+enc);
    String plainAfter=cryptoUtil.decrypt(key, enc);
    System.out.println("Original text after decryption: "+plainAfter);
 }
}

答案 2 :(得分:1)

大多数团队将JSON文件保留在版本控制之外并手动添加到环境中。

虽然与保持环境变量中的值不完全相同,但它具有类似的安全性。只能访问版本控制的开发人员无法访问密钥,开发人员也不会意外地使用生产密钥在自己的系统上运行。但另一方面:有权访问生产服务器的人可以在两种情况下获得密钥。

答案 3 :(得分:0)

请阅读以下为firebase_admin.credentials.Certificate()提供的文档。

fd

因此,您可以通过传递已解析的密钥文件内容中的字典来创建证书证书。密钥文件的内容可以来自加密的环境变量值。使用此凭据来初始化应用程序。