Showing posts with label Cryptography. Show all posts
Showing posts with label Cryptography. Show all posts

Wednesday, March 21, 2012

Base64 Encoding Binary Files in PT 8.52

My blog contains a handful of posts showing various methods for base64 encoding data (both plain text and binary). While these procedures apply universally, my primary motivation was (and is) integration: sending base64 encoded binary data to other systems. Today I was looking through the 8.52 Integration Broker PeopleBooks and noticed a handful of new features to support binary file operations:

What about files uploaded by users? How do you work with uploaded files? Uploaded files are stored as file attachments in either a database record or FTP destination. The File Attachment API provides the GetAttachment and PutAttachment PeopleCode functions to help you move files in and out of file attachment repositories.

As of 8.52, it seems we no longer need to use the Apache Commons Codec Java library to convert binary files into base64 or base64 into binary files. Likewise, prior to 8.52, if someone asked me if PeopleCode could create a binary file, I would say, "No, but you can use the Java API classes in PeopleCode to write binary files." Now I can say, "Yes, but you will first have to convert your binary data to base64." Hmmm... still sounds a bit funny. Is it better? I think so. Reading and writing binary was the hard part. With 8.52, I have that functionality without having to maintain an extra Java library in my class path. Besides pluggable encryption, as I and my readers demonstrated in my post Base64 Encoding for PeopleSoft, there are a number of ways to encode data including using database features and delivered Java methods (for SQL Server, see sachin's comments here and here).

Wednesday, January 20, 2010

Base64 Encoding Binary Files

Several months ago I posted a handful of methods for base64 encoding strings. At that time I suggested that the next important step was base64 encoding binary files. This represents a challenge because PeopleCode does not offer methods for reading binary files. The following PeopleCode uses the Java API and the Apache Commons Codec library to show how to base64 encode a binary file. To run this example, download the commons-codec library and place the jar file in your local %PS_HOME\class directory. Next, create an App Engine with a PeopleCode step and insert the following PeopleCode:

Local JavaObject &f_in = CreateJavaObject("java.io.FileInputStream", "c:\temp\binaryfile.gif");
Local JavaObject &coder_in = CreateJavaObject("org.apache.commons.codec.binary.Base64InputStream", &f_in, True);
Local JavaObject &reader = CreateJavaObject("java.io.BufferedReader", CreateJavaObject("java.io.InputStreamReader", &coder_in));

Local string &b64Data = "";
Local any &line;

While True
&line = &reader.readLine();
If (&line <> Null) Then
&b64Data = &b64Data | &line | Char(13) | Char(10);
Else
Break;
End-If;

End-While;

Local File &b64File = GetFile("c:\temp\base64_encoded.txt", "A", "A", %FilePath_Absolute);
&b64File.WriteLine(&b64Data);
&b64File.Close();

The Java objects at the top of this listing read binary data from a file and transform that data into a base64 text listing, which is then stored in the variable &b64Data. For demonstration purposes, I wrote the contents of &b64Data to a file. In real life, you would add this contents to a CDATA node prior to sending a message through the Integration Broker.

Before running this code, replace c:\temp\binaryfile.gif with the full path to a real binary file on your workstation. After adding the PeopleCode listed above, disable restart and run your App Engine. When the App Engine completes, look in your local c:\temp directory for a file named base64_encoded.txt.

Monday, May 04, 2009

Base64 Encoding with Pluggable Encryption

I dedicate this post to Chris Heller since he is the person that pointed me at PeopleSoft's pluggable encryption in his comments to my last post on Base64 Encoding for PeopleSoft. I had not considered PeopleSoft's pluggable encryption for base64 encoding. Thanks Chris!

Before we can base64 encode strings with PeopleSoft's Pluggable Encryption, we need to create an Algorithm Chain and an Encryption Profile. PeopleBooks does an excellent job of explaining each of these terms as well as telling how to create each of these components. Since PeopleSoft delivers the base64 encryption algorithm with the PSPETSSL encryption library, we can move right into defining the Algorithm Chain. To define the chain, navigate to PeopleTools > Security > Encryption > Algorithm Chain. Add the new value BASE64_ENCODE and add the following Algorithms in order:

  1. PSUnicodeToAscii
  2. base64_encode
  3. PSAsciiToUnicode

Be sure to set the sequence number for each row (1-3). Save and navigate to PeopleTools > Security > Encryption > Encryption Profile. Add the new value BASE64_ENCODE. Specify the algorithm chain BASE64_ENCODE and save. We can now test this pluggable encryption profile with a little PeopleCode:

Local object &crypto = CreateObject("Crypt");
&crypto.Open("BASE64_ENCODE");
&crypto.UpdateData("Hello World");
MessageBox(0, "", 0, 0, "Encrypted: " | &crypto.Result);

And, the result should be: SGVsbG8gV29ybGQ=. I am sure you will all agree that this solution is much simpler than the ugly Java Reflection I previously demonstrated. Furthermore, this method is well documented in PeopleBooks and supported by PeopleSoft.

If I am reading PeopleBooks right, Pluggable Encryption only works with ASCII text. That is why we have to use the PSUnicodeToAscii and PSAsciiToUnicode algorithms in our algorithm chain. What this means is that we still need to use an alternative for base64 encoding binary data, a requirement when embedding binary data in XML documents.

Sunday, May 03, 2009

Base64 Encoding for PeopleSoft

This week on the ittoolbox peopeltools-I forum, I ran across a question on base64 encoding binary data using PeopleCode. There are a couple of ways to base64 encode data, none of which are delivered. Before deciding which method to use, we first have to answer two questions:

  1. Where is the data (file, database, text string)?
  2. Is the data in binary or plain text format?

If the data resides in the database, then we can select it out using the following SQL:

SELECT UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW('Hello World'))) FROM DUAL;

In fact, rewrite that as:

SELECT UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(%TextIn(:1)))) FROM DUAL;

And, if you are an Oracle database user, then you have a generic SQL statement you can use to base64 encode any plain text. If you name the SQL definition BASE_64_ENCODE, then you can call that SQL definition as follows:

Local string &source = "Hello World";
Local string &encoded;
Local number &i;

SQLExec(SQL.BASE_64_ENCODE, &source, &encoded);
MessageBox(0, "", 0, 0, "base64: " | &encoded);

The PeopleCode and SQL above will work for any text string as long as you are using an Oracle database. Now, what if you want to base64 encode binary data? If the binary data is in the database, then modify the SQL accordingly.

Did you notice the %TextIn Meta-SQL I sneaked into the SQL above? Use it whenever you are sending large amounts of text to the database. For a string as short as "Hello World," you don't need it, but if you are trying to base64 encode an entire XML document, then you might.

What if you are not using Oracle database? Perhaps T-SQL has a base64 encoding routine? If so, great. If not, then the following code provides a Java/PeopleCode solution. Unfortunately, the 2 common Java base64 encoding algorithms use method and constructor overloads in a manner that requires some ugly Java reflection PeopleCode.

REM ** create an instance of the Java base64 encoder;
Local JavaObject &encoder = CreateJavaObject("sun.misc.BASE64Encoder");

REM ** get a reference to a Java class instance for the primitive byte array;
Local JavaObject &arrayClass = GetJavaClass("java.lang.reflect.Array");
Local JavaObject &bytArrClass = &arrayClass.newInstance(GetJavaClass("java.lang.Byte").TYPE, 0);

REM ** use reflection to get a reference to the method we want to call;
Local JavaObject &encodeArgTypes = CreateJavaObject("java.lang.Class[]", &bytArrClass.getClass());
Local JavaObject &encodeMethod = &encoder.getClass().getMethod("encode", &encodeArgTypes);

REM ** call the method;
Local JavaObject &bytes = CreateJavaObject("java.lang.String", "Hello World").getBytes();
Local JavaObject &result = &encodeMethod.invoke(&encoder, CreateJavaObject("java.lang.Object[]", &bytes));

REM ** print the result;
MessageBox(0, "", 0, 0, "Result: " | &result.toString());

If you know enough about Java to compile a wrapper class and you have access to your app server's class directory, then you can eliminate the Java reflection code in favor of a wrapper:

package com.blogspot.jjmpsj;

import java.io.IOException;

import sun.misc.BASE64Encoder;

public class Base64Coder {
public static String encodeString(String data) {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data.getBytes());
}

public static String encodeBytes(byte[] data) {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
}

... and call it like so:

Local JavaObject &encoder = GetJavaClass("com.blogspot.jjmpsj.Base64Coder");
Local string &result = &encoder.encodeString("Hello World");

MessageBox(0, "", 0, 0, &result);

As you can see from this final example, when working with overloaded constructors and methods, Java wrappers dramatically simplify PeopleCode.

Each of the examples above uses "Hello World" as the text string. What is "Hello World" in base64? SGVsbG8gV29ybGQ=. You can find an online base64 encoder/decoder here.

I will have to leave the question of base64 encoding binary files for another day. I am on the East coast attending Collaborate '09 and I need to get some rest so I can be right as rain for the conference tomorrow morning.

Update May 4th, 2009 at 4:09 AM: In the comments to this post, devwfb suggests that developers use org.apache.commons.codec.binary.Base64
rather than sun.misc.BASE64Encoder because classes in the sun package are undocumented and unsupported. devwfb is right and I should have mentioned this fact in my original post. I chose to use sun's base64 encoder because it is delivered and doesn't require customers to add more jars to the $PS_HOME/class directory. I will post the commons-codec example and link to it from here. See Sun's FAQ for more information.