views:

3209

answers:

1

First and foremost, I am not a Java programmer. I'm looking for an example solution to this problem because the Java developer I have does not have much experience working with encryption. Everything we've found online pertains to encrypting web pages and dealing with the MS keystore. We just want to work with a single string from PowerBuilder (PB) and be able to decrypt it in Java. The restriction here is the MS library. Due to certain limitations, we are stuck with using this method of encrypting so it's up to the Java side to handle what's being thrown at it.

What I have is a PB version 10.2 program that needs to call this Java utility and pass it a username & password. We are trying to encrypt the password as a command line friendly string as that is how PB will make the call to the Java app.

In PB I'm using the following object: http://www.topwizprogramming.com/freecode_crypto.html

What the code is doing, is wrapping the Microsoft cryptographic API found in advapi32.dll. The functions it uses are:

CryptAcquireContext http://msdn.microsoft.com/en-us/library/aa379886(VS.85).aspx

CryptCreateHash http://msdn.microsoft.com/en-us/library/aa379908(VS.85).aspx

CryptHashData http://msdn.microsoft.com/en-us/library/aa380202(VS.85).aspx

CryptDeriveKey http://msdn.microsoft.com/en-us/library/aa379916(VS.85).aspx

CryptEncrypt http://msdn.microsoft.com/en-us/library/aa379924(VS.85).aspx

It's using the Microsoft Strong Cryptographic Provider and PROV_RSA_FULL. The code takes the data to be encrypted, converts it to a BLOB which is then passed to the encryption functions. There, it acuires a context, creates a hash object from the context, hashes the password, gets a session key from the hash, then calls encrypt/decrypt. Last thing is it takes the BLOB returned and converts it to a string under the ANSI character set.

There are a number of constants which at a glance I understand where some come from, others not so much: Constant String KEY_CONTAINER = "MyKeyContainer" Constant ULong PROV_RSA_FULL = 1 Constant ULong CALG_MD5 = 32771 Constant ULong CALG_RC4 = 26625 Constant ULong ENCRYPT_ALGORITHM = CALG_RC4 Constant ULong CRYPT_NEWKEYSET = 8 Constant ULong ERROR_MORE_DATA = 234

Whether this is done in 1.5 using something like BouncyCastle or 1.6 with the Sun crypto interface for MS I don't care, we're just dying to see this work and are honestly over our heads.

+1  A: 
import java.security.GeneralSecurityException;
import java.security.MessageDigest;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Decrypt
{

  public static void main(String... argv)
    throws Exception
  {
    byte[] password = "password".getBytes("UTF-8");
    byte[] ciphertext = { -68, -112,  66, 78,   85,   50, 22, -63, 
                           16,   24, -45,  4, -116,  -14, 88,  34, 
                          -85,  116, 105, 59,   45, -126 };
    byte[] plaintext = decrypt(password, ciphertext);
    System.out.println(new String(plaintext, "UTF-8"));
  }

  public static byte[] decrypt(byte[] password, byte[] ciphertext)
    throws GeneralSecurityException
  {
    MessageDigest digest = MessageDigest.getInstance("MD5");
    byte[] hash = digest.digest(password);
    Cipher rc4 = Cipher.getInstance("RC4");
    rc4.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash, "RC4"));
    return rc4.doFinal(ciphertext);
  }

}

N.B.:

This "encryption" is horrible. RC4 is a key stream cipher. Never use the same keystream cipher key for more than one message! Using the same password in this way for multiple messages makes it trivial to recover the plain text and the key given multiple cipher texts. Given the weakness in MD5, they can probably recover the password too. These flaws are enough to compromise a good stream cipher, but RC4, like MD5, has its own vulnerabilities and is not recommended for new applications.

I'm sure that you knew all that, and are constrained by some legacy application, but if other people see this answer, they need to understand that the PowerBuilder "crypto" library you are compelled to use is incompetently implemented.


Since cipher input and output is always "binary", a text encoding is commonly used, such as Base-64 or Base-85 when the cipher text has to pass through a text-oriented channel (like the command line). If possible, can you Base-64 encode the cipher text before invoking the Java utility? That would insulate you from any character encoding issues.

erickson
Thanks erickson. The only issue we're having at this point is the character set. Converting the argument with Cp1252 gets us 99% there but some combos with spaces in the original data give us garbage. We're looking into other ways of dealing with this. Thanks very much.