TryCatchFinally.net Some SQL, some .NET, and whatever else

7Aug/120

Decrypting RSA with Java

In a recent Java project (a *small* departure from my normal VB.NET development), I was attempting to use RSA to decrypt a value stored in one of our databases, but was running into some trouble. When I used Java's native RSA Cipher (available in Java 1.5+), I could decrypt the value without any issues, but when I switched to Bouncycastle, I would get gibberish. Since I was doing the decryption from inside an Oracle database, the only version of Java available was 1.4.2, which doesn't have a default RSA provider, leaving Bouncycastle as the only option.

The decryption didn't fail or throw an exception - it always succeeded - but the resulting decrypted byte array was completely different between the two providers. In Java's native RSA, it was 32 bytes (as it should be), but in Bouncycastle, it was 128 bytes (the same length as the input, interestingly).

In the end, it turned out that Java’s default RSA implementation is "RSA/None/PKCS1Padding", whereas BC’s is “RSA/None/NoPadding”. Changing BC’s version of the Cipher.getInstance line in my code to explicitly specify the new padding resolved my issue:

Here's the original code (Line 10 is the one to switch out):

Cipher RSADecrypter;

// Here's the flag for choosing which provider to use
Boolean UseBouncyCastle = Boolean.TRUE;

// Choose between Java and BouncyCastle
if (UseBouncyCastle == Boolean.TRUE)
{
    Security.addProvider(new BouncyCastleProvider());
    RSADecrypter = Cipher.getInstance("RSA", "BC");
} else
{
    RSADecrypter = Cipher.getInstance("RSA");
}

// Initialize the Cipher using our the first key in the keystore
// This step works fine for both providers
RSADecrypter.init(Cipher.DECRYPT_MODE, keystore.getKey("1", PrivateKeyPassword.toCharArray()));

// Decrypt first 128 bytes of the array - here's the problem
// Java RSA gives 32 byte result, BouncyCastle gives 128 bytes of randomness
aegEncryptionKey = RSADecrypter.doFinal(binaryDataEncrypted,0,128);

More related reading:

How to check the java version
Bouncycastle's default crypto
Java's default crypto

26May/114

Generate an x509 certificate with an SHA256 signature hash

When authenticating with a vendor using a custom webservice, the vendor requested that we use an x509 certificate with a 2048 byte key and an SHA256 hash (sometimes referred to as SHA2, though SHA2 actually refers to the group of hashes containing SHA256, 384, and 512). Since I'd used IIS to generate our certificate (IIS will only generate a certificate using an SHA1 hash), and it involved quite a bit of research to get a certificate with an SHA256 signature hash on it, I wanted to detail the steps here:

  1. First, download and install OpenSSL from Shining Light. The "Light" version of the package will do, since you're only using basic functionality.
  2. Generate your Certificate request (CSR), specifying an SHA256 signature hash
    1. You'll be prompted for a few certificate fields, including your state, company name, computer name on your certificate, etc. Enter these as they come up.
  3. This will generate two files - PrivateKey.key (which contains the un-encrypted version of your private key - protect this file, as somebody who obtains it along with your signed public key can impersonate you), and CertificateRequest.csr (your certificate signing request, which is not sensative).
  4. Though this isn't required, if you want to confirm what you're entered in the CSR, you can view the details using another OpenSSL command line
  5. Now that you have your CSR, submit it to whatever signing authority you use - for us, it was Verisign, but there are any number of different CAs out there that can sign it.
  6. Once your CA has signed the CSR, you'll get back either a binary p7b file (which we'll called SignedKeyFromCA.p7b) containing your certificate signed public key (and, possibly, the certificate chain your CA used as well), or either a binary or base64 CER file containing just your certificate. Whatever you receive back, you'll need to convert it to a Base64 CER (called SignedKeyFromCA.cer here), since that's what OpenSSL expects.
  7. To combine your private key with the signed public key to create a certificate:
    1. Since you're exporting your private key in this file, you'll be required to encrypt it with a password, which OpenSSL will prompt you for (twice).
  8. You've now got your signed key pair - SignedKeyPair.p12. You can either use this pkcs12 file in your code, or you can import in into your web server (assuming it supports SHA256 hashes). IIS7, for example, supports importing this certificate and using it for SSL, but just doesn't support generating a SHA2 CSR in the first place.

Enjoy! If you have any issues, please feel free to post a comment and I'll do my best to answer it!

2Jul/090

Encrypting data per-user in your .NET application without asking for a secret

When you encrypt something, you need some kind of secret to do so - a password that's used to encrypt and decrypt your message, or a public/private key like a certificate. In order to get a secret, you usually have to ask the user for one - in most cases, a password - before they can decrypt their data and access it.

I had an application where I wanted to encrypt some database connection details between sessions. This is easy enough, but I wanted to do it without having to ask the user for a password - I just wanted it done transparently. I thought this meant storing a secret key somewhere, but there's no safe place to keep a key -even if it's embedded in your code, somebody who really wants it will always be able to get it. What I needed was a private place to store a key, where no other Windows user on the same computer would be able to access it.

That's when I discovered the DPAPI - it's been around since .NET 2.0, and it's available through the System.Security.Cryptography namespace. What's great about this cryptographic feature is that you can have Windows encrypt something with the same key it uses to encrypt files with EFS - a private user key that's based on a hash of their Windows password. This means that you don't need to ask the user for a secret - you've already got one handy, and Windows will prevent other users of the system from being able to decrypt your data. The downside of this is that if the user resets their password, you're toast - if you're only keep the data as a convenience (as I am), that's no problem, but if you can't afford to lose the data, you'll need to ask the user for the secret instead of relying on this method.

This MSDN article that detailed its use, and after adding some code that made it easy to encrypt and decrypt strings, here's what I ended up with:

Imports System
Imports System.Security.Cryptography

''' <summary>
''' Encrypts and Decrypts information using the current Windows user key
''' </summary>
''' <remarks></remarks>
Public Class DPAPI

    Private Shared EntropyString As String = "Some value I made up"

    ' Create byte array for additional entropy when using Protect/Unprotect method.
    Private Shared Function AdditionalEntropy() As Byte()
        Dim encoder As New System.Text.ASCIIEncoding
        Return encoder.GetBytes(EntropyString)
    End Function

#Region " Encrypt "

    Public Shared Function Protect(ByVal data() As Byte) As Byte()
        Try
            ' Encrypt the data using DataProtectionScope.CurrentUser. The result can be decrypted
            ' only by the same current user.
            Return ProtectedData.Protect(data, AdditionalEntropy, DataProtectionScope.CurrentUser)
        Catch e As CryptographicException
            Console.WriteLine("Data was not encrypted. An error occurred.")
            Console.WriteLine(e.Message.ToString())
            Throw
        End Try
    End Function

    Public Shared Function ProtectString(ByVal data As String) As String

        Dim encoder As New System.Text.ASCIIEncoding
        Return Convert.ToBase64String(Protect(encoder.GetBytes(data)))

    End Function

#End Region

#Region " Decrypt "

    Public Shared Function Unprotect(ByVal data() As Byte) As Byte()
        Try
            'Decrypt the data using DataProtectionScope.CurrentUser.
            Return ProtectedData.Unprotect(data, AdditionalEntropy, DataProtectionScope.CurrentUser)
        Catch e As CryptographicException
            Console.WriteLine("Data was not decrypted. An error occurred.")
            Console.WriteLine(e.Message.ToString())
            Throw
        End Try

    End Function

    Public Shared Function UnprotectString(ByVal data As String) As String

        Dim encoder As New System.Text.ASCIIEncoding
        Dim b() As Byte = Convert.FromBase64String(data)
        Return encoder.GetString(Unprotect(b))

    End Function

#End Region

End Class

Download the code here

To use the class, just change the entropy value at the top (this is combined with the user's key to create a new key used to encrypt your data), call Protect() or ProtectString() and pass it the required data, and you're good to go! I added the ProtectString and UnprotectString because I was storing the values in the user's app.config file, and needed an easy string representation.

If you use the class or have any questions, please let me know!