Reproducing Javascript’s escape() function in Groovy

From time to time, I’ve seen people looking for a way to reproduce Javascript’s escape() function in this or that language. It’s not a complex function – escape() simply replaces characters that aren’t alphanumeric (and a few symbols) with their Unicode values in hexadecimal. Anyway, I recently had a need to escape something using Groovy. Here’s the method.


/* A method to mimic Javascript's escape() function.
* It replaces 'special characters' with their Unicode hexadecimal
* equivalent. This simple method is not designed to handle
* double byte character sets. Feel free to improve it.
*/
String escapeLikeJavascript(String sourceText) {
def specialCharsRegex = /[^\w@*-+.\/]/
return sourceText.replaceAll(specialCharsRegex, {
"%${Integer.toHexString(it.codePointAt(0)).toUpperCase().padLeft(2, '0')}"
})
}

June 9, 2011 at 4:21 pm 1 comment

How to configure Notepad++ to always open in a new window

I tend to work with multiple documents open in front of me at once, so I can compare or copy code snippets from different sources. I generally use Notepad++ as my text editor, but the default behavior for Notepad++ is to open multiple documents in tabs within a single instance. There are ways to change that behavior, and every time I change computers or reinstall Notepad++, I find myself having to go look them up again. So here they are for my own convenience and maybe yours.

If you’re launching Notepad++ from a command line or a shortcut, you can simply add -multiInst as a command line argument.

Notepad++ Command Line Switches

However, that doesn’t alter the behavior when you open a document from the Windows Explorer context menu by clicking “Edit with Notepad++”. A more global way of making the application open in a new instance every time is to place an empty file named asNotepad.xml in the Notepad++ Program Files directory.

Notepad++ Configuration Files

February 24, 2011 at 11:36 am Leave a comment

Burst.net / Nocster.net

Friendly to spammers? Or just terrible customer service?


Followup: Burst.net responded on 3/24/2011, over a month after I submitted the support request.

Matt W has responded to your ticket.

Please preserve the subject line. This is important.

———————————————–
(Matt W)
———————————————–
As far as I know that is not a violation of our AUP. I’m forwarding this ticket to one of our abuse representatives now for confirmation.


Thank you,

Matt W.
Technical Support Representative
BurstNET Technologies, Inc.
Technical Support is always available via http://support.burst.net

February 23, 2011 at 10:25 am Leave a comment

Why You Should Never Set GROOVY_HOME

January 31, 2011 at 10:30 am Leave a comment

Configuring Luna HSM Software on AIX

We’re using a Luna HSM with Tomcat running on IBM AIX to sign PDFs in a web application. There were a few extra steps required to get the Luna software working with Java that the setup instructions did not mention, so I’m documenting them here.

The LunaSA software installed to /usr/lunasa. The Java Security Provider API for the Luna includes three files, which are found in /usr/lunasa/jsp/lib. They are libLunaAPI.so, the native (C++ I presume) library for accessing the Luna; and LunaJCASP.jar and LunaJCESP.jar, which are the security providers for Java.

First, we verified that we had a randomness generator configured in the right directory, as described by the Luna setup instructions. Then we installed the software. Next we configured a trust relationship between the Luna and our server by exchanging certificates, as per instructions.

The additional steps were setting some environment variables for the user account that Tomcat runs with:

JAVA_OPTS should include -Djava.library.path=/usr/lunasa/jsp/lib/. Putting the Luna library directory on java.library.path makes the native libLunaAPI.so library available to the Java Native Interface. If it is not there, any application trying to use the Luna API will throw an ”UnsatisfiedLinkException”.

LIBPATH should be set and should include /usr/lunasa/jsp/lib/. This is another way of getting the native Luna API library on java.library.path. It may not be necessary if the path is set in JAVA_OPTS.

CLASSPATH should be set and should include /usr/lunasa/jsp/lib/LunaJCASP.jar and /usr/lunasa/jsp/lib/LunaJCESP.jar. Contrary to our expectations, just having the jar files in the /lib folder of our war file didn’t seem to make the jar files available to our web app. Adding them to the Tomcat user’s classpath made everything work.

We are using the 64-bit Luna software with IBM Java 6, 64-bit.

September 29, 2010 at 9:35 am Leave a comment

Signature With Timestamp Using iText and Luna HSM


import java.security.KeyStore
import java.security.MessageDigest
import java.security.PrivateKey
import java.security.cert.Certificate
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfStamper
import com.itextpdf.text.pdf.PdfSignatureAppearance
import com.itextpdf.text.pdf.PdfName
import com.itextpdf.text.Image
import com.itextpdf.text.Rectangle
import com.itextpdf.text.pdf.PdfEncryptor
import com.itextpdf.text.pdf.PdfWriter
import com.itextpdf.text.pdf.PdfSignature
import com.itextpdf.text.pdf.PdfDate
import com.itextpdf.text.pdf.TSAClient
import com.itextpdf.text.pdf.TSAClientBouncyCastle
import com.itextpdf.text.pdf.PdfPKCS7
import com.itextpdf.text.DocumentException
import com.itextpdf.text.pdf.PdfDictionary
import com.itextpdf.text.pdf.PdfString
import com.chrysalisits.crypto.LunaTokenManager



String hsmPartitionLabel = "Luna_partition_name"
String hsmPassword = "partition_password"
String hsmKeyLabel = "private_key_alias"
String hsmCertLabel = "certificate_alias"
String hsmCALabel = "CA_certificate_alias"
String timestampUrl = "URL_of_timestamp_server"
String ownerPassword = "PDF_owner_password"
String inFile = "path_to_unsigned_PDF"
String reason = "signature_reason"
String location = "signature_location"
String contact = "signature_contact_email"
String sealPath = "path_to_an_image_to_be_used_with_signature"
String outFile = "path_to_put_signed_PDF"

// Login to HSM
LunaTokenManager tm = LunaTokenManager.getInstance()
tm.Login(hsmPartitionLabel, hsmPassword)

// Dynamically load security providers
Class providerClass = Class.forName("com.chrysalisits.crypto.LunaJCAProvider")
java.security.Provider provider = (java.security.Provider)providerClass.newInstance()
java.security.Security.removeProvider(provider.getName())
java.security.Security.insertProviderAt(provider, 2)
providerClass = Class.forName("com.chrysalisits.cryptox.LunaJCEProvider")
provider = (java.security.Provider) providerClass.newInstance()
java.security.Security.removeProvider(provider.getName())
java.security.Security.insertProviderAt(provider, 3)

// This syntax gets an instance of a LunaKeystore
KeyStore ks = KeyStore.getInstance("Luna")
ks.load(null, null)
PrivateKey key = (PrivateKey) ks.getKey(hsmKeyLabel, null)
// We need to assemble the certificate chain manually because the HSM doesn't support the
// getCertificateChain method.
Certificate[] chain = new Certificate[2]
chain[0] = ks.getCertificate(hsmCertLabel)
chain[1] = ks.getCertificate(hsmCALabel)

// It seems necessary to load the file into the PdfReader this way to
// avoid a java.io.IOException in sun.nio.ch.FileChannelImpl on AIX.
byte[] content = new File(inFile).readBytes()
PdfReader reader = new PdfReader(content, ownerPassword.getBytes())
FileOutputStream fout = new FileOutputStream(outFile)
// Third param is PDF revision (char).
// Groovy thinks '' is a GString, so we have to be explicit and force it to char.
PdfStamper stp = PdfStamper.createSignature(reader, fout, ''.toCharacter().charValue(), null, true)
PdfSignatureAppearance sap = stp.getSignatureAppearance()
// Instead of reason and location, a graphic image will be rendered. Reason and
// location will still be shown in the signature properties.
sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC)
Image image = Image.getInstance(sealPath)
sap.setSignatureGraphic(image)
// sap.setVisibleSignature(new Rectangle(x-len, y-len, x-loc, y-loc), page, null for new fieldname)
// Coordinates begin from lower left. Units are 1/72 of an inch. 8.5 x 11 in == 612 x 792
sap.setVisibleSignature(new Rectangle(36, 36, 100, 100), 1, null)
sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED)

PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached"))
dic.setReason(reason)
dic.setLocation(location)
dic.setContact(contact)
dic.setDate(new PdfDate(sap.getSignDate()))
sap.setCryptoDictionary(dic)

// This is estimated space for the signature itself.
int contentEstimated = 15000
HashMap exc = new HashMap()
exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2))
sap.preClose(exc)

// make the digest
InputStream data = sap.getRangeStream()
MessageDigest messageDigest = MessageDigest.getInstance("SHA1")
byte[] buf = new byte[8192]
int n
while ((n = data.read(buf)) > 0) {
messageDigest.update(buf, 0, n)
}
byte[] hash = messageDigest.digest()
Calendar cal = Calendar.getInstance()

// If we add a time stamp:
TSAClient tsc = null
String tsa_url = timestampUrl
// Our provider does not use userid and password; use
// TSAClientBouncyCastle(tsa_url, tsa_userid, tsa_password)
// if yours does.
tsc = new TSAClientBouncyCastle(tsa_url)

byte[] ocsp = null

// Create the signature
PdfPKCS7 sgn = new PdfPKCS7(cert.key, cert.chain, null, "SHA1", null, false)
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, ocsp)
sgn.update(sh, 0, sh.length)
byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, tsc, ocsp)

if (contentEstimated + 2 < encodedSig.length)
throw new DocumentException("Not enough space")

byte[] paddedSig = new byte[contentEstimated]
System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length)
// Replace the contents
PdfDictionary dic2 = new PdfDictionary()
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true))
sap.close(dic2)

tm.Logout()

println "Signed PDF saved as ${outFile}."

September 13, 2010 at 2:40 pm Leave a comment

Signing a PDF with iText and a Luna HSM

After obtaining a trial of a Certificate for Adobe CDS, which is only delivered on a hardware storage module (HSM) or a USB key, I had some difficulties adapting the iText examples for signing a PDF to work with the API for the HSM. The code below is the result of trial-and-error. It is based on the examples provided for iText at http://itextpdf.com/book/examples.php#chapter12.

I have been told that there is a naming convention for certificates on the HSM that, if followed, makes the KeyStore.getCertificateChain() method usable: The labels for each certificate should all start the same way (in this case, it would be ‘mycert’) and followed by the suffix ‘–cert#’, where # is the number of the certificate in the chain. In this instance, it would be something like this:

  • mycert–cert0 (Adobe CDS certificate)
  • mycert–cert1 (GlobalSign CA for Adobe certificate)
  • mycert–cert2 (Adobe Root CA certificate)

If it works, that should make it unnecessary to create the certificate chan manually, as is done in this example.

This example uses Groovy, but it should translate almost directly to Java.


import java.security.KeyStore
import java.security.MessageDigest
import java.security.PrivateKey
import java.security.cert.Certificate
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfStamper
import com.itextpdf.text.pdf.PdfSignatureAppearance
import com.itextpdf.text.pdf.PdfName
import com.itextpdf.text.Image
import com.itextpdf.text.Rectangle
import com.itextpdf.text.pdf.PdfEncryptor
import com.itextpdf.text.pdf.PdfWriter
import com.itextpdf.text.pdf.PdfSignature
import com.itextpdf.text.pdf.PdfDate
import com.itextpdf.text.pdf.TSAClient
import com.itextpdf.text.pdf.TSAClientBouncyCastle
import com.itextpdf.text.pdf.PdfPKCS7
import com.itextpdf.text.DocumentException
import com.itextpdf.text.pdf.PdfDictionary
import com.itextpdf.text.pdf.PdfString
import com.chrysalisits.crypto.LunaTokenManager



String hsmPartitionLabel = "Luna_partition_name"
String hsmPassword = "partition_password"
String hsmKeyLabel = "private_key_alias"
String hsmCertLabel = "certificate_alias"
String hsmCALabel = "CA_certificate_alias"
String ownerPassword = "PDF_owner_password"
String inFile = "path_to_unsigned_PDF"
String reason = "signature_reason"
String location = "signature_location"
String contact = "signature_contact_email"
String sealPath = "path_to_an_image_to_be_used_with_signature"
String outFile = "path_to_put_signed_PDF"

// Login to HSM
LunaTokenManager tm = LunaTokenManager.getInstance()
tm.Login(hsmPartitionLabel, hsmPassword)

// Dynamically load security providers
Class providerClass = Class.forName("com.chrysalisits.crypto.LunaJCAProvider")
java.security.Provider provider = (java.security.Provider)providerClass.newInstance()
java.security.Security.removeProvider(provider.getName())
java.security.Security.insertProviderAt(provider, 2)
providerClass = Class.forName("com.chrysalisits.cryptox.LunaJCEProvider")
provider = (java.security.Provider) providerClass.newInstance()
java.security.Security.removeProvider(provider.getName())
java.security.Security.insertProviderAt(provider, 3)

// This syntax gets an instance of a LunaKeystore
KeyStore ks = KeyStore.getInstance("Luna")
ks.load(null, null)
PrivateKey key = (PrivateKey) ks.getKey(hsmKeyLabel, null)
// We need to assemble the certificate chain manually because the HSM doesn't support the
// getCertificateChain method.
Certificate[] chain = new Certificate[2]
chain[0] = ks.getCertificate(hsmCertLabel)
chain[1] = ks.getCertificate(hsmCALabel)

// It seems necessary to load the file into the PdfReader this way to
// avoid a java.io.IOException in sun.nio.ch.FileChannelImpl on AIX.
byte[] content = new File(inFile).readBytes()
PdfReader reader = new PdfReader(content, ownerPassword.getBytes())
FileOutputStream fout = new FileOutputStream(outFile)
// Third param is PDF revision (char).
// Groovy thinks '' is a GString, so we have to be explicit and force it to char.
PdfStamper stp = PdfStamper.createSignature(reader, fout, ''.toCharacter().charValue(), null, true)
PdfSignatureAppearance sap = stp.getSignatureAppearance()
sap.setCrypto(cert.key, cert.chain, null, PdfSignatureAppearance.WINCER_SIGNED)
// Instead of reason and location, a graphic image will be rendered. Reason and
// location will still be shown in the signature properties.
sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC)
sap.setReason(reason)
sap.setLocation(location)
sap.setContact(contact)
Image image = Image.getInstance(sealPath)
sap.setSignatureGraphic(image)
// sap.setVisibleSignature(new Rectangle(x-len, y-len, x-loc, y-loc), page, null for new fieldname)
// Coordinates begin from lower left. Units are 1/72 of an inch. 8.5 x 11 in == 612 x 792
sap.setVisibleSignature(new Rectangle(36, 36, 100, 100), 1, null)
stp.close()

tm.Logout()

println "Signed PDF saved as ${outFile}."

September 13, 2010 at 2:22 pm Leave a comment

Material Spam: Opting Out of AJC Reach

It seems like AJC Reach, the division of the Atlanta Journal-Constitution that deposits a roll of ads on our driveways, has upped their delivery rate to twice per week recently. I’m getting a little tired of picking up soggy litter from my driveway, so I’ve been trying to find out who to contact to stop deliveries.

I used the contact form provided on the AJC Reach website, but that didn’t seem to get any results. Luckily, I found a number for the Senior VP for Circulation at the AJC posted at StopAJCReach.org and was able to contact a person in his office who took down my address and name and promised to have the deliveries stopped. Maybe this will be useful to others who want to opt out too.

August 16, 2010 at 3:16 pm Leave a comment

Browser Redisplaying Old PDFs: How to Manage Caching of Dynamically Generated Documents

Last year, I shared an approach for serving PDFs or other content over SSL from a Grails application in a way that wouldn’t fail due to browser caching constraints.

We have used the same approach in several of our controllers to share PDF reports of different kinds, and recently, we encountered a related problem: when users requested different PDFs in a short enough time frame, their browser was serving up cached versions of previous PDFs. To the user, it appeared like the web application was sending them an unrelated report.

Once I identified the problem as browser caching – which was not immediately evident! – I developed an approach to solve the problem.

First, I shortened the amount of time the browser was instructed to cache the content. In my previous example, the browser was instructed to keep the downloaded PDF for 5 minutes:


response.setHeader("Cache-Control", "private")
Calendar cal = Calendar.getInstance()
cal.add(Calendar.MINUTE,5)
response.setDateHeader("Expires", cal.getTimeInMillis())

I changed the time to 10 seconds.

I also reasoned that if the filename of each PDF is unique, the browser will not mistake a new PDF for an old one. In our code, we were assigning a fixed name to all output:

response.setHeader("Content-Disposition", "inline; filename=report.pdf")

Also, the URL was always the same – and some browsers ignore the content-disposition and name downloaded files using the URI. (E.g., http://host/contextRoot/pageName becomes pageName.pdf.)

I used a Grails hack to provide a unique URI for every file: in place of supplying a name:value parameter on the querystring (e.g., http://somepath?id=18), Grails lets you pass an id parameter on the URL itself (e.g., http://somepath/18). By including a unique value in the id parameter, even one that isn’t used by my application, we fool browsers into thinking that each PDF download is unique. In other words, http://somepath/18 gets saved as 18.pdf. I also put a unique name in the content-disposition header.

In this example from our web application, we are displaying the PDF and making it available for download by embedding it with an object tag in another html page. This is where I added the id parameter:


<object type="application/pdf"
name="showpdf" id="showpdf"
data="${createLink(action: 'getPDF', params:[id: params.unqid?.encodeAsHTML()])}"
style="width: 800px; height: 800px; border: solid 1px #cccccc;
background: url(${createLinkTo(dir: 'images', file: 'docloading.png')})
top center no-repeat;">
<p class="error">It appears that your web browser is not configured to display PDF files.
<g:link controller="userHelp" action="index">Click</g:link> for more information.</p>
</object>

Note that this object tag calls the getPDF action to supply the content of the object. Here’s the getPDF action:


def getPDF = {
try {
if (!session.pdffile) {
throw new Exception("Document path not found on session. Unable to retrieve report.")
}
def unqid = params.id
if (!unqid) {
throw new Exception("Unique report id not provided in parameters.")
}
def pdfFile = new File(session.pdffile)
def b = pdfFile.readBytes()
response.setContentType("application/pdf")
response.setHeader("Content-Disposition", "inline; filename=${unqid}.pdf")
response.setContentLength(b.length)
response.setHeader("Pragma", "")
response.setHeader("Cache-Control", "private")
Calendar cal = Calendar.getInstance()
cal.add(Calendar.SECOND, 10)
response.setDateHeader("Expires", cal.getTimeInMillis())
response.outputStream << b
} catch (Throwable t) {
log.fatal """Failure retrieving report document""".toString(), t
flash.error = "Failure retrieving report document: ${t.message}"
render(view: "errframe")
}
}

Between the shortened expiration time and providing a unique filename, I think we covered every case where a client browser might re-serve old downloads. At least, this was tested and worked on IE 8, Chrome, and Firefox 3.6.7.

[October 4, 2010] Postscript: Further experimenting has shown that Grails seems to ignore anything on the URL after the action name. I’m able to name the file anything I want using this expression for the URL to the file: ${createLink(action: 'getPDF')}/${filename}. I don’t have to resort to using an id param.

July 26, 2010 at 1:03 pm Leave a comment

Split a PDF into pages with PdfStamper (iText)

More help from the iText Questions List:

Finding signature fields in a split page

June 21, 2010 at 2:07 pm Leave a comment

Older Posts


Categories

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 1 other follower


Follow

Get every new post delivered to your Inbox.