在源中存储密码

时间:2015-12-17 06:04:59

标签: cryptography passwords password-protection

我现在已经阅读了几个地方,基本上建议不要在源代码中存储密码,我理解原因。

我有两个应用程序:一个用于加密,一个用于解密。数据被加密到服务器上的文件;此文件在客户端的计算机上下载并解密,然后由专有应用程序(不是我的)处理。数据是敏感的,(理想情况下)只能由处理应用程序访问。 目前我正在使用对称密钥算法,因为数据足够大。密码作为字符串硬编码到源代码中 - 我知道,有几个原因很糟糕。

我想知道的是存储密码的最佳方法是什么?我的想法是使用非对称密钥算法,例如RSA,只是为了密码,但无法解决如何做到这一点,如果它在我的场景中甚至有意义。我不想引入另一个文件进行分发。我不太了解反编译,但我认为在客户端应用程序中实现PBKD会带来同样的问题。正如你所知,密码学对我来说是新的,并且使用这个伟大的论坛。

2 个答案:

答案 0 :(得分:0)

请不要在源文件中使用对称密钥。您可以在不引入其他文件的情况下使用RSA。与对称密钥相反,如果可以保证源文件的完整性,则可以在源代码中对公钥进行硬编码,而不会出现任何安全问题。然后,如果有人设法改变它,解密将不起作用或正在进行中间人(MITM)攻击(你不会知道它)。

CAVEATS

  • 这种方法遭受MITM攻击的易感性。一个适当的对策是签署数据,但是你被困在处理密钥,实际上是一个单独的问题。
  • 键不是永远的,您生成的密钥对需要不时旋转。
  • 使密钥足够大(至少2048位)。
  • 撤销需要由您手动完成。如果您需要更自动化的解决方案,请考虑查看PKI和OSCP响应者(使用或不使用装订)。
  • 如果您发送的数据很大,则RSA操作会很长。

好的,不管怎样,让我们​​深入研究。示例代码完全使用JDK 8编写。

首先,生成密钥对。这需要一次或每次需要旋转时完成:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = kpg.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();

byte[] pubEnc = publicKey.getEncoded();
byte[] privKeyEnc = privateKey.getEncoded();

System.out.println(Base64.getEncoder().encodeToString(pubEnc));
System.out.println(Base64.getEncoder().encodeToString(privKeyEnc));

假设公钥是(这些是我生成的实际密钥,使用这些密钥):

private static final String PUBLIC_KEY_BASE64 = 
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGue
5TCdPJt08w7crbvWjfcSUy/xXjzjjjjPDP7D8PNSnn
CUeNcGWsR/Pd3eoBjmrAy/4Rl8JlHylRry8pX7Zpcz
iQB8wWQdpkSoArjeu4taeFn/45+eg4J5mzmIzFG9F5
wF7N+SeSvtq3E3Q0mtJRRZZJYgNkFmeDuOQjljJVZw
IDAQAB";

,私钥是:

private static final String PRIVATE_KEY_BASE64 = 
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAg
EAAoGBAIa57lMJ08m3TzDtytu9aN9xJTL/FePOOOOM
8M/sPw81KecJR41wZaxH893d6gGOasDL/hGXwmUfKV
GvLylftmlzOJAHzBZB2mRKgCuN67i1p4Wf/jn56Dgn
mbOYjMUb0XnAXs35J5K+2rcTdDSa0lFFlkliA2QWZ4
O45COWMlVnAgMBAAECgYBAWTIRi1ISuHEkh48qoS8j
+eCwmNGVuvvFA55JUSdVVikrZm08iwCk5sD9qW6JS8
KFT2mMcZWxws5za171PffbeHoIFaNI5n5OXJa4meZA
cgl4ae5su89BjvfzDF2gsnBHwLpgsT0aVdIDQ5BGtL
WzRwZCogY6lZhBOQZAaNFYIQJBALr2+kT+pkSlxrrR
tMxK7WL+hNO7qOIl/CTBuAa6/zTtoEjFMFQBY//jH+
6iabHDfKpaFwh6ynZTXZsb7qIKOM8CQQC4eRAH1VEo
iofZNnX3VjiI5mLtV8rc8Jg+wznN+WFnwdNoLK8y9t
EcuKxg3neIJAM70D6l0IhBfza1QAqQh4/pAkA5vyLZ
wJV2SoWxGihvmQztMJOyGho1j2nrqHHAkm1U2bhSAa
XFrJBIbsxkFoHyx+BvdVf75IE4PtOAnwX7wpB9AkBQ
7CKBHS2N+D8hpQdYqcUBIPdyoFmIVC6lEaTw2x3Ekz
027KsqUyVmUQilMdIDsbCNc4uX14N+H90S43X+8sjJ
AkAKsvRbZ0Au9JytSRKSB7vYl37283zHoQ5jyYUE7x
g7C6nWSl1GEa6khZ47hFAj9C2bdLJ6GtjTleFsVCsR
LUoG";

由于格式原因,上面的代码行换行。

假设args[0]"attack at dawn!",您的客户端看起来像这样:

public static void main(String[] args) throws Exception {
   byte[] pubKey = Base64.getDecoder().decode(PUBLIC_KEY_BASE64);
   PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubKey));

   Cipher cipher = Cipher.getInstance("RSA");
   Cipher.init(Cipher.ENCRYPT_MODE, publicKey);
   String encryptedData = Base64.getEncoder().encodeToString(cipher.doFinal(args[0].getBytes("UTF-8")));
   System.out.println("Encrypted data is: " + encryptedData);
}
  

输出:加密数据是:   cDoPpQYc6qibrBl5jdENcV + g6HslQDlo9potca5rQxecnxR3Bd / e1T0njqUMACl7x7AG3foGxqZyyUIMrOVXcnw / ux7BgQcg + RDZhSVFQAd5kUGI96pw8WtDVo1N1 + WEfaaPhK9CpwUKUxtwR0t27n + W0vhFCqNTEGhofLt8u9o =

服务器:

public static void main(String[] args) throws Exception {
  byte[] privKey = Base64.getDecoder().decode(PRIVATE_KEY_BASE64);
  PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privKey));

  Cipher cipher = Cipher.getInstance("RSA");
  cipher.init(Cipher.DECRYPT_MODE, privateKey);
  byte[] encryptedData = Base64.getDecoder().decode(args[0]); 
  byte[] decryptedData = cipher.doFinal(encryptedData);
  System.out.println("Decrypted message was: " + new String(decryptedData, "UTF-8"));

}
  

输出:解密的信息是:黎明时袭击!

JDK的RSA实现的一个很酷的事情是它使用来自PKCS#1的最佳非对称加密填充(OAEP),其效果是甚至相同的消息看起来与每次加密不同。

与之前的评论者提到的一样,如果没有全面了解,可能还有其他安全问题需要解决。但是对于它的价值,我认为这种方法比使用对称密钥更好,对称密钥仍然可以用于这种加密方案。然后你会得到所谓的混合密码系统,但有些API可以帮你做到这一点。

答案 1 :(得分:-2)

不是将明文密码存储在源中并检查,而是可以将加密密码存储在源中(硬编码)。

然后用户可以输入密码,代码对密码进行加密,并将加密结果与存储的加密值进行比较,以查看密码是否匹配。输入的密码(不是加密密码)然后用于加密/解密数据。

使用的加密应该努力将加密密码反转为明文密码很难/不可能。

示例:

  1. 您选择加密/解密密码为This is my password1(当前位于来源中的密码)。
  2. 您使用SHA-256加密密码,并在源代码中对其进行硬编码:9845735b525fa70b2651975022a44be268af1d4defadba9ab2a0301e0579534c
  3. 加密/解密应用提示输入密码。
  4. 用户输入密码。
  5. App计算输入密码的SHA-256,并将结果与​​9845735b525fa70b2651975022a44be268af1d4defadba9ab2a0301e0579534c
  6. 进行比较
  7. 如果匹配,则使用您已安装的代码,使用输入的密码This is my password1对数据进行加密/解密。
  8. 如果不匹配,请输入错误。
  9. 黑客获取您的源并可以访问哈希9845735b525fa70b2651975022a44be268af1d4defadba9ab2a0301e0579534c和您使用的算法。但他需要This is my password1密码来解密数据。他可以反向或暴力破解哈希,但需要花费精力和时间。

    人们可以使用种子等,但是为了保密它们也是同样的问题。

    它可能不是最安全的,我不是专家,但它比在源中拥有This is my password1更好。