Openbravo软件及其衍生物(例如,unicentaopos)具有以下加密实现,以将数据库密码存储在一个简单的配置文件中。
package com.openbravo.pos.util;
import java.io.UnsupportedEncodingException;
import java.security.*;
import javax.crypto.*;
/**
*
* @author JG uniCenta
*/
public class AltEncrypter {
private Cipher cipherDecrypt;
private Cipher cipherEncrypt;
/** Creates a new instance of Encrypter
* @param passPhrase */
public AltEncrypter(String passPhrase) {
try {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(passPhrase.getBytes("UTF8"));
KeyGenerator kGen = KeyGenerator.getInstance("DESEDE");
kGen.init(168, sr);
Key key = kGen.generateKey();
cipherEncrypt = Cipher.getInstance("DESEDE/ECB/PKCS5Padding");
cipherEncrypt.init(Cipher.ENCRYPT_MODE, key);
cipherDecrypt = Cipher.getInstance("DESEDE/ECB/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, key);
} catch (UnsupportedEncodingException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
}
}
/**
*
* @param str
* @return
*/
public String encrypt(String str) {
try {
return StringUtils.byte2hex(cipherEncrypt.doFinal(str.getBytes("UTF8")));
} catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException e) {
}
return null;
}
/**
*
* @param str
* @return
*/
public String decrypt(String str) {
try {
return new String(cipherDecrypt.doFinal(StringUtils.hex2byte(str)), "UTF8");
} catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException e) {
}
return null;
}
}若要加密,请使用以下内容(只加密密码):
config.setProperty("db.user", jtxtDbUser.getText());
AltEncrypter cypher = new AltEncrypter("cypherkey" + jtxtDbUser.getText());
config.setProperty("db.password", "crypt:" + cypher.encrypt(new String(jtxtDbPassword.getPassword())));若要解密,请使用以下内容:
String sDBUser = m_App.getProperties().getProperty("db.user");
String sDBPassword = m_App.getProperties().getProperty("db.password");
if (sDBUser != null && sDBPassword != null && sDBPassword.startsWith("crypt:")) {
AltEncrypter cypher = new AltEncrypter("cypherkey" + sDBUser);
sDBPassword = cypher.decrypt(sDBPassword.substring(6));
}我正在C#中开发一个独立的软件模块,我想从该配置文件中读取数据库密码。对如何做到这一点有什么建议吗?
通过分析代码,我可以推断出:
db.password=crypt:XXX
其中XXX是加密密码。
请帮我想出密码的解密方法。关于实际读取普通文件的帮助是不必要的。请假定我已经将用户名和加密密码(没有"crypt:“部分)存储在C#程序中的变量中。
我一直在尝试修改关于类似问题的现有示例,但它们集中在AES上,到目前为止,我在这方面还没有成功。
基本上,应该在C#中构建以下函数:
private string DecryptPassword(string username, string encryptedPassword)我该怎么做?
该软件是开源的,可以找到这里。
一个测试用例:DecryptPassword("mark", "19215E9576DE6A96D5F03FE1D3073DCC")应该返回密码getmeback。基础密码为cypherkeymark。我在不同的机器上测试过,使用相同的用户名,“哈希”密码总是一样的。
发布于 2016-01-31 22:00:52
AltEncrypter从密码中派生密钥的方法非常糟糕。不应使用这种方法。
首先,它不安全。密钥派生算法是不安全的,除非它是计算密集型。相反,可以使用像scrypt、bcrypt或PBKDF2这样的算法。
其次,SHA1PRNG算法没有得到很好的定义。说“它使用SHA-1”是不够的。哈希每隔多长时间执行一次?这是不标准化的;您将无法在另一个平台上请求一个"SHA1PRNG“(如.Net),并获得相同的输出。
因此,放弃这种加密方法,使用一些由有知识的人编写和维护的简单、安全的东西。
不幸的是,问题并没有就此结束。AltEncrypter实用程序是以最糟糕的方式使用的,使用的密钥不是秘密的,可以可逆地加密身份验证密码。这根本不安全。它允许攻击者解密用户密码,并对其他系统上的用户帐户使用这些密码。
就像这个系统的作者想要制造一场安全灾难。
发布于 2016-01-31 21:56:04
这是张便条。我不能添加注释,但我认为用于加密的算法不是SHA1。它是“DESEDE/ECB/PKCS5Padd”,看看要加密的密码创建(获取) cipherEncrypt =cipherEncrypt的行
SHA1PRNG是一个伪随机数生成器,用于生成加密过程中使用的第一个随机数,以便生成“不同”加密,即使同一纯文本被加密。
另一件重要的事情是用来加密的钥匙,我的意思是:
KeyGenerator kGen = KeyGenerator.getInstance("DESEDE");
kGen.init(168, sr);
Key key = kGen.generateKey(); <-- this key此密钥用于加密和解密,但我看不到它的存储位置。我的意思是它每次都会再生。应该从某个地方存储和检索它,而不是重新生成它,因为如果没有使用相同的密钥,就不可能解密任何密码文本。
发布于 2016-02-01 17:46:18
这是一个使用一些解决办法的答案。
我尝试过重新实现SHA1PRNG,提供了GNU实现(即露天矿),但是它并没有给出与适当的SUN实现相同的结果(所以它们不是不同的,就是我以错误的方式实现的)。因此,我已经实现了一个解决方案:调用一个java程序来为我们派生密钥。是的,这很便宜,但只是暂时的工作。如果有人看到了我的SHA1PRNG实现中的错误,请告诉我。
首先,这里有一个简单的Java程序,它将使用SHA1PRNG生成器导出一个给定种子的168位密钥。只需将其输出到stdout上,空间分开。
import java.io.UnsupportedEncodingException;
import java.security.*;
import javax.crypto.*;
public class PasswordDeriver {
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
if(args.length == 0){
System.out.println("You need to give the seed as the first argument.");
return;
}
//Use Java to generate the key used for encryption and decryption.
String passPhrase = args[args.length-1];
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(passPhrase.getBytes("UTF8"));
KeyGenerator kGen = KeyGenerator.getInstance("DESEDE");
kGen.init(168, sr);
Key key = kGen.generateKey();
//Key is generated, now output it.
//System.out.println("Format: " + key.getFormat());
byte[] k = key.getEncoded();
for(int i=0; i < k.length; i++){
System.out.print(String.format((i == k.length - 1) ? "%X" : "%X ", k[i]));
}
}
}这将保存为PasswordDeriver.java,使用javac <file>编译,然后将产生的PasswordDeriver.class放在与此编译程序相同的文件夹中:(实际的C#程序)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;
using System.Diagnostics;
namespace OpenbravoDecrypter
{
class Program
{
static void Main(string[] args)
{
var decrypted = Decrypt("19215E9576DE6A96D5F03FE1D3073DCC", "mark");
Console.ReadLine();
}
static string Decrypt(string ciphertext, string username)
{
//Ciphertext is given as a hex string, convert it back to bytes
if(ciphertext.Length % 2 == 1) ciphertext = "0" + ciphertext; //pad a zero left is necessary
byte[] ciphertext_bytes = new byte[ciphertext.Length / 2];
for(int i=0; i < ciphertext.Length; i+=2)
ciphertext_bytes[i / 2] = Convert.ToByte(ciphertext.Substring(i, 2), 16);
//Get an instance of a tripple-des descryption
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Mode = CipherMode.ECB; //ECB as Cipher Mode
tdes.Padding = PaddingMode.PKCS7; //PKCS7 padding (same as PKCS5, good enough)
byte[] key_bytes = DeriveKeyWorkAround(username);
Console.WriteLine("Derived Key: " + BitConverter.ToString(key_bytes));
//Start the decryption, give it the key, and null for the IV.
var decryptor = tdes.CreateDecryptor(key_bytes, null);
//Decrypt it.
var plain = decryptor.TransformFinalBlock(ciphertext_bytes, 0, ciphertext_bytes.Length);
//Output the result as hex string and as UTF8 encoded string
Console.WriteLine("Plaintext Bytes: " + BitConverter.ToString(plain));
var s = Encoding.UTF8.GetString(plain);
Console.WriteLine("Plaintext UTF-8: " + s);
return s;
}
/* Work around the fact that we don't have a C# implementation of SHA1PRNG by calling into a custom-prepared java file..*/
static byte[] DeriveKeyWorkAround(string username)
{
username = "cypherkey" + username;
string procOutput = "";
//Invoke java on our file
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c java PasswordDeriver \"" + username + "\"";
p.StartInfo.RedirectStandardOutput = true;
p.OutputDataReceived += (e, d) => procOutput += d.Data;
p.StartInfo.UseShellExecute = false;
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
//Convert it back
byte[] key = procOutput.Split(' ').Select(hex => Convert.ToByte(hex, 16)).ToArray();
return key;
}
/* This function copies the functionality of the GNU Implementation of SHA1PRNG.
* Currently, it's broken, meaning that it doesn't produce the same output as the SUN implenetation of SHA1PRNG.
* Case 1: the GNU implementation is the same as the SUN implementation, and this re-implementation is just wrong somewhere
* Case 2: the GNU implementation is not the same the SUN implementation, therefore you'd need to reverse engineer some existing
* SUN implementation and correct this method.
*/
static byte[] DeriveKey(string username)
{
//adjust
username = "cypherkey" + username;
byte[] user = Encoding.UTF8.GetBytes(username);
//Do SHA1 magic
var sha1 = new SHA1CryptoServiceProvider();
var seed = new byte[20];
byte[] data = new byte[40];
int seedpos = 0;
int datapos = 0;
//init stuff
byte[] digestdata;
digestdata = sha1.ComputeHash(data);
Array.Copy(digestdata, 0, data, 0, 20);
/* seeding part */
for (int i=0; i < user.Length; i++)
{
seed[seedpos++ % 20] ^= user[i];
}
seedpos %= 20;
/* Generate output bytes */
byte[] bytes = new byte[24]; //we need 24 bytes (= 192 bit / 8)
int loc = 0;
while (loc < bytes.Length)
{
int copy = Math.Min(bytes.Length - loc, 20 - datapos);
if (copy > 0)
{
Array.Copy(data, datapos, bytes, loc, copy);
datapos += copy;
loc += copy;
}
else
{
// No data ready for copying, so refill our buffer.
Array.Copy(seed, 0, data, 20, 20);
byte[] digestdata2 = sha1.ComputeHash(data);
Array.Copy(digestdata2, 0, data, 0, 20);
datapos = 0;
}
}
Console.WriteLine("GENERATED KEY:\n");
for(int i=0; i < bytes.Length; i++)
{
Console.Write(bytes[i].ToString("X").PadLeft(2, '0'));
}
return bytes;
}
}
}您可以看到标准的内容,比如初始化一个tripple密码提供程序,给它一个密钥,并在其中计算密文的解密。它还包含当前中断的SHA1PRNG实现和解决方案。假定java位于当前环境变量的PATH中,该程序将生成以下输出:
派生密钥: 86-EF-C1-F2-2F-97-D3-F1-34-49-23-89-E3-EC-29-80-02-92-52-40-49-5D-CD-C1 明文字节: 67-65-74-6D-65-62-61-63-6B 明文UTF-8: getmeback
因此,这里有一个解密函数(加密它将相同,只需将.CreateDecryptor()更改为.CreateEncryptor())。如果您忘记了执行密钥派生的代码,则解密代码只在大约20行代码中工作。因此,在回顾中,我的回答是一个起点,其他人谁想让这个解决方案100% C#。希望这能有所帮助。
https://stackoverflow.com/questions/35119827
复制相似问题