How to decrypt in Java a AES256 encrypted file by GnuPG? - java

I'm have encrypted a text file on Linux using:
gpg --cipher-algo AES256 -c file.txt
That command asks for a passphrase, let's say we enter "123" here.
This file can be trivially decrypted:
gpg -d file.txt.gpg
Now I like to decrypt this file in Java, but can't quite find out how to do this using the passphrase "123". Specifically, it's not entirely clear what the salt and initial vector is, and what else is needed.

GnuPG implements the OpenPGP protocol, which is not directly support by Java's native classes. OpenPGP has its own file format, but also uses a slightly different variant of the CFB mode.
Instead of implementing all that on your own, better go for the Bouncy Castle library. It also provides an example how to decrypt a symmetrically encrypted message, which boils down to those relevant calls to decrypt an OutputStream out (some more code to determine the used algorithm parameter and compression is also provided in the linked example):
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(algorithm).setSecureRandom(
new SecureRandom()).setProvider("BC"));
encGen.addMethod(
new JcePBEKeyEncryptionMethodGenerator(passPhrase).setProvider("BC"));
OutputStream encOut = encGen.open(out, compressedData.length);

Related

Encrypt user passwords when uploading code and decrypt during run-time

Have to begin moving in-house, local machine only Java framework into a private but online repo for staff only, however, the current framework requires a properties class which includes sensitive information (usernames, passwords etc) to login to the database and other technology the framework uses.
As a result, pushing the latest framework will include all of said individuals usernames/passwords, which is a security concern.
I've been asked to "Encrypt the password so when it's uploaded it's hidden" and then "Decrypt the password during runtime, so that information can be used to execute the code, as without the login data, the framework will not work".
While there is much better solutions (ie just don't upload the properties class, rework framework to not need the properties class etc), this is apparently unacceptable as it "works this way".
I'm struggling on the logic of this request, but furthermore how this task could actually be accomplished.
There's a few solutions I pinged about having it in a private notepad on your system only, but again apparently unacceptable.
It "needs to just be within a new Java Class".
Is this request possible, and if so, what sort of approach could I take? So the sensitive information within the properties class is encrypted if someone downloads the framework, but when executed, that information is decrypted and the framework runs. Framework is being uploaded to Bitbucket.
(Not asking for the code, just asking for help working out how this could be achieved, if at all, because it seems to be a very strange request or am I missing something?)
Here is the snippet to upload your data to the server. os must be a valid output stream used for upload.
OutputStream os = ... /* your upload stream */
OutputStream gzipos = new GZIPOutputStream(os);
CryptoAlgorithm = "RSA";
Cipher cipher = Cipher.getInstance(CryptoAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, PuK);
CipherOutputStream cos = new CipherOutputStream(gzipos, cipher);
DataOutputStream dos = new DataOutputStream(cos);
dos.write... /* use it as a normal data output stram /*
I use DataOutputStream because I used binary data in my implementation. Still, you can also use a normal writer:
Writer w = new OutputStreamWriter(cos);
And, for the download snippet:
CryptoAlgorithm = "RSA";
...
Cipher cipher = Cipher.getInstance(CryptoAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, PK);
CipherInputStream cis = new CipherInputStream(new GZIPInputStream(is), cipher);
DataInputStream dis = new DataInputStream(cis);
If you used a writer, obviously you'll need to change the last declaration accordingly:
Reader r = new InputStreamReader(cis);
Of course you need a Key Pair of
PrivateKey PK;
PublicKey Puk;
Those two needs to be saved locally (you need to chose how to, just google it and chose how you want to save them, there're several options).
Now, not knowing how well you know RSA, I'll explain a couple of things:
IMPORTANT before goint to production make sure to have your key pair saved and backupped! Multiple times in different media, if possible. If you lose those keys, your data becomes unreadable, thus lost forever!
Really, protect those keys with evertything you've got!
you use the PublicKey to crypt because this way only those who have the private key can uncrypt it, because, normally, you can distribute the PublicKey, and everybody with the public key can decrypt what was crypted with the PriveteKey. This is used for electronic signatures because only the owner of the PrivateKey can write a document that can be opened with the relative PublicKey. On the opposite, everybody can use the PublicKey to cipher a document but only the one with the associated private key can read it. That's why, if you store protected data, you use the public key to cipher and the private to uncipher. Right now it doesn't matter to you, but better be prepared for the future.
you can use the algorithm you fancy, but the most used is RSA and it's the one you can find the most informations on.
I compress the data before chiphering it not for saving space, but to maximize the randomization of the data, thus making harder to brute-force decipher them. Still, there's a problem: GZip protocol puts GZ before any file compressed with said protocol as signature. You can manually remove that signature before ciphering and add it manually after deciphering, but it become a little more complex to handle. You can bypass the compression part if you want (or find another algorithm to compress/uncompress).

Unable to decrypt csv file using symmetric key java

I am provided two files encrypted_key.enc and encrypted_data.csv.enc. I need to use my private key to decrypt the encrypted_key.enc to get a symmetric key and then use that symmetric key to decrypt the encrypted_data.csv.enc file.
On the terminal, the following commands get the job done:
openssl rsautl -decrypt -ssl -inkey my_private_key -in encrypted_key.enc -out key
openssl aes-256-cbc -d -in encrypted_data.csv.enc -out secret.txt -pass file:key
My goal is to perform the java equivalent of the two commands. I was able to successfully decrypt the first file and retrieve the symmetric key.
Now I'm unable to use that symmetric key to decrypt the csv file. My issue arises in the decipher.init(Cipher.DECRYPT_MODE, keySpec); I receive the following stacktrace
Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default parameters
I'm unclear on what exactly I'm missing from the decryption process. I've tried changing the cipher provider but that didn't help. Other posts have posted solutions using an IVParameterSpec but my decryption case doesn't seem to need it or I'm confused on where to put it.
File file = new File("my_private_key");
PrivateKey pk = getPrivateKey(file);
// Decrypt secret key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pk);
File skFile = new File("encrypted_key.enc");
FileInputStream fileInputStream = new FileInputStream(skFile);
byte[] decodedBytes = IOUtils.toByteArray(fileInputStream);
byte[] original = cipher.doFinal(decodedBytes);
String decodedOriginal = new String(Base64.encodeBase64(original));
System.out.println(decodedOriginal);
// Use the secret key for decrypting file
File csvFile =
new File(
"encrypted_data.csv.enc");
FileInputStream csvIS = new FileInputStream(csvFile);
Cipher decipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(original, "AES");
decipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] csvOriginal = decipher.doFinal(IOUtils.toByteArray(csvIS));
String csvContents = new String(csvOriginal);
System.out.println(csvContents);
Before Java 1.8 (I think, somewhere around there) you are limited by the Java Unlimited Strength Policy for key sizes above 128-bits. This is the most likely cause of the exception you are getting.
Unfortunately this won't fix your code. openssl with the pass flag uses an insecure KDF named EVP_BytesToKey(). Java doesn't natively support this KDF. You don't want to use it anyway since it is insecure. Update the upstream code to use a better KDF like PBKDF2. There is native support for this in Java.
Further, you're using CBC mode in openssl and ECB mode in Java. And you aren't specifying an IV in openssl. I get the impression you didn't write the Java code yourself. You might benefit from taking the time to learn and research what is actually happening in your code and in the commands you are executing and you might be better equipped to solve the problem.

RSA encryption in Java, decrypt in PHP

Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?

Equivalent of PHP's crypt function in Java

I am migrating my PHP code to Google App Engine - Java.
So I need an equivalent of PHP's crypt function in Java,
since I have stored all the passwords of registered users
using crypt in my DB.
Edit 1:
Here is my php code for encrypting passwords :
$password = "test123";
$pwd = crypt($password,$password);
echo $pwd;
Output is (On Windows as well as a linux based server on HostMonser):
temjCCsjBECmU
Can someone give me equivalted java code?
I have tried various permutations & combinations with
MessageDigest class, but can't get it right..
Edit 2:
Here is sample code which I thought would work but did not:
try {
{
String password = "test123";
MessageDigest digest = MessageDigest.getInstance( "MD5" );
byte[] passwordBytes = password.getBytes( );
digest.reset( );
digest.update( passwordBytes );
digest.update( passwordBytes );
byte[] message = digest.digest( );
StringBuffer hexString = new StringBuffer();
for ( int i=0; i < message.length; i++)
{
hexString.append( Integer.toHexString(
0xFF & message[ i ] ) );
}
String encrypted = hexString.toString();
System.out.println(encrypted);
} } catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
This is an old thread but I ran into the same issue and found a different solution. You can use the UnixCrypt/Md5Crypt classes in the Apache Commons Codec 1.7 library.
For example you can call
UnixCrypt.crypt(string, salt)
OR
Md5Crypt.md5Crypt(byte[], salt)
I haven't looked into the other encryption types but I imagine their are other utilities as well.
org.apache.commons.codec.digest.UnixCrypt
org.apache.commons.codec.digest.Md5Crypt
You have to know what implementation of PHP crypt has been used (MD5? SHA256? SHA512?) because there are several, depending on your OS : http://php.net/manual/fr/function.crypt.php
The Java equivalent class is MessageDigest. When you create an instance of this class, you provide the hash algorithm, for example :
MessageDigest md = MessageDigest.getInstance("MD5");
MessageDigest md2 = MessageDigest.getInstance("SHA-256");
MessageDigest md3 = MessageDigest.getInstance("SHA-512");
// etc.
byte[] encryptedPassword = md.digest("yourPassword".getBytes());
It seems you have to work with a legacy database already populated with passwords you cannot discard, so you can't just switch to a salted MessageDigest, preferably using SHA-1. And your problem gets more complicated, since PHP's crypt is a wrapper that might use one of several algorithms. But let's assume your PHP uses the original DES-based UNIX crypt, then all you need is an implementation of that in Java. As far as i know, there is no implementation of UNIX's crypt in the standard Java installation, but you might want to look here for a list of options.
You need to take a look at the java.security classes (what used to tbe the JCE):
In there you'll find everything you need to do what you want (depending on which algorithm you need).
http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/security/package-summary.html
e.g. MessageDigest for MD5/SHA etc:
http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/security/MessageDigest.html
Check these against the Google App Engine whitelist here, I'm not sure what's supported and what isn't.
http://code.google.com/appengine/docs/java/jrewhitelist.html
The java.security stuff can be a bit of a pain to work with sometimes, you may alternatively want to use Jasypt - which is a more simplified API that works with any JCE:
http://www.jasypt.org/
PHP's crypt supports multiple hash functions. If you use the MD5 version (hash starts with $1$), you can find a Java implementation here,
http://www.java2s.com/Open-Source/Java-Document/Groupware/LibreSource/md5/MD5Crypt.java.htm
Please notice that they use their own MD5 class. I am not sure if it's the same as standard MD5.
I am sure you can find Java implementation for other hash algorithms too.
Well, PHP's crypt isn't actually encryption as far as I know. It's just a wrapper around some one-way hashing functions I believe, so if your current PHP site's using crypt's MD5 or SHA256 or whatever, I'd expect that you could find those equivalent hashing classes/functions in Java.
I can recommend this: MD5Crypt implementation
MD5Crypt.crypt("youPassword"); // output: $1$zSiw0koU$i3Srfmoxx4VPePJHWqAuK/
This is one of the few implementations, which works for me.

PKI verification across Java and Python

I am trying to implement a PKI verification scheme, where a message string is signed with a private key on server, the signature is stored on the client along with the message string. The client then verifies the signature using a public key.
The restrictions of my environment are, the server is Google App Engine and the client is a Java program. I have played with Java-only and Python-only solutions of PKI verification and got them to work, however when doing one operation in Python and another in Java is posing problem, mainly due to Key file format restrictions and my limited understanding of cryptography terminology.
One of the biggest limitations is crypto support in GAE. The only library supported is PyCrypto and this library can't read public/private keys stored in PEM, DER or X509 formats. As far as I could find, only M2Crypto supports reading from these files, but it can't be used inside GAE because it's a wrapper around openssl, so not a pure python solution. Even if I could find a way to translate the public/private keys from PEM/DER/X509 to the format that PyCrypto understands, that will work for me. But I couldn't find any way to do it. Any ideas there?
I found one possible solution in the form of tlslite. tlslite could read a private key from PEM file and create a signature. Here is the code.
from tlslite.utils.cryptomath import bytesToBase64
from tlslite.utils.keyfactory import parsePEMKey
s = open('private.pem').read()
key = parsePEMKey(s)
doc = 'Sample text'
bytes = array('B')
bytes.fromstring(doc)
print bytesToBase64(key.sign(bytes))
The corresponding Java code I used to verify the signature is.
String signAlgo = "SHA1WithRSAEncryption";
// read public key from public.der
byte[] encodedKey = new byte[294]; // shortcut hardcoding
getAssets().open("public.der").read(encodedKey);
// create public key object
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
// read signature (created by python code above)
byte[] encodedSig = new byte[345];
getAssets().open("signature.txt").read(encodedSig);
byte[] decodedSig = Base64.decodeBase64(encodedSig);
// Do verification
Signature verifyalg = Signature.getInstance(signAlgo);
verifyalg.initVerify(pk);
verifyalg.update(message.getBytes());
Log.d(TAG, "Verif : "+verifyalg.verify(decodedSig));
The verification fails.
I suspected if the tlslite is using different algorithm for signature creation than what the java code expects.
So I tried to find that out.
On python side
print key.getSigningAlgorithm()
gave me
pkcs1-sha1
on Java side, I tried to find all supported algorithms with this code:
Set<String> algos = java.security.Security.getAlgorithms("Signature");
for(String algo : algos) {
Log.d(TAG, algo);
}
That gave me
MD4WithRSAEncryption
RSASSA-PSS
SHA1withDSA
SHA1withRSA/ISO9796-2
1.2.840.113549.1.1.10
SHA512withRSA/PSS
MD5withRSA/ISO9796-2
DSA
SHA512WithRSAEncryption
SHA224withRSA/PSS
NONEWITHDSA
SHA256withRSA/PSS
SHA224WithRSAEncryption
SHA256WithRSAEncryption
SHA1withRSA/PSS
SHA1WithRSAEncryption
SHA384withRSA/PSS
SHA384WithRSAEncryption
MD5WithRSAEncryption
I tried all the SHA1 values on the Java side. But none helped to verify the signature generated by tlslite with pkcs1-sha1 algo. Any idea about this mapping?
These are different operations. In Python, you need to use hashAndSign. The default happens to be SHA1 hash.
Keyczar should work fine on App Engine, and is available in both Java and Python flavours.

Categories

Resources