Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Securing WebStart Deployments: Signing and Sandboxing

Posted on July 27, 2010 By Luis Fernandez

Filed from a late night desk with too much coffee. Oracle badges are new at JavaOne, plugin popups are still a thing, and Web Start is how we ship desktop muscle without MSI drama.

Dialogue

Dev: Our Java Web Start app needs to save files, talk to a REST endpoint, and print. I can ship it right now.

Sec: Ship what, exactly. Unsigned jars will live in the sandbox. Signed jars asking for everything will make users click a scary dialog. Pick your poison.

Dev: So the plan is either stay in the sandbox and use the JNLP services, or sign and ask for full power. Anything in between

Sec: The in between is signing the jars but still running sandboxed. That lets you use the Web Start services without the full trust prompt. If you really need disk and sockets anywhere, then you go all permissions with a real code signing cert.

Dev: Okay. Show me receipts.

Evidence: what signing and sandboxing do for you

– Unsigned jars run in the default sandbox. No raw file IO, no random sockets, no printing. You do get the JNLP services like FileOpenService, FileSaveService, PersistenceService, and ClipboardService.

– Signed jars without all permissions still run sandboxed but can use the JNLP services. This is a sweet spot for many apps that only need user approved file pickers and limited storage.

– Signed jars with all permissions get the keys to the house. That also triggers the trust dialog. If you sign with a CA issued code signing cert, users see the publisher name and can make an informed choice. Self signed makes the dialog look scary and support gets angry calls.

– Timestamps matter. If you sign with a timestamp, your signature keeps working after the cert expires. If you skip it, the signature dies with the cert and your app fails to launch on a later date. Your pager will not like that.

– Mixed jars bite. If you mix signed and unsigned jars in the same codebase, you invite classloader drama and warnings. Sign everything or nothing for a given app.

– The plugin and Web Start on current Java 6 updates are stricter about chain validation and name matching. Broken chains and weird Distinguished Names trigger harder prompts or flat stops. Keep your keystore clean and consistent.

Bottom line: if you can live with the sandbox plus JNLP services, you get fewer scary prompts and still deliver a smooth user story. If you really need disk, sockets anywhere, or native, go all in on signing with a real cert and timestamp it.

How I wired it

Goal: Ship a Web Start app that can open and save files through the user picker and talk to a known HTTPS endpoint. No raw disk writes. No scary prompt.

1. Create a keystore for dev

# one time for your dev box
keytool -genkeypair \
  -alias devsigner \
  -keyalg RSA -keysize 2048 -validity 3650 \
  -keystore devsigner.jks

# optional: view it
keytool -list -v -keystore devsigner.jks

This is only for local testing. For real shipping bits, buy a code signing certificate from a known CA and store it safely.

2. Sign every jar and add a timestamp

# sign and timestamp your jars
jarsigner -keystore devsigner.jks \
  -tsa http://timestamp.verisign.com/scripts/timstamp.dll \
  dist/myapp-core.jar devsigner

jarsigner -keystore devsigner.jks \
  -tsa http://timestamp.verisign.com/scripts/timstamp.dll \
  dist/myapp-ui.jar devsigner

# verify signatures
jarsigner -verify -verbose -certs dist/myapp-core.jar

For production signing, point -keystore to the CA issued cert and keep the private key offline. Use timestamping so signatures survive cert expiry.

3. Write the JNLP with a clear security posture

<jnlp spec="1.0+" codebase="https://download.example.com/apps/myapp" href="myapp.jnlp">
  <information>
    <title>MyApp</title>
    <vendor>Acme Corp</vendor>
    <description>Web Start editor for power users</description>
    <icon href="myapp.ico"/>
    <offline-allowed/>
  </information>

  <security>
    <!-- omit all-permissions to stay sandboxed but signed -->
    <!-- <all-permissions/> only if you truly need it -->
  </security>

  <update check="background" policy="always"/>

  <resources>
    <j2se version="1.6+"/>
    <jar href="myapp-core.jar" main="true"/>
    <jar href="myapp-ui.jar"/>
  </resources>

  <application-desc main-class="com.acme.myapp.Main"/>
</jnlp>

Signed jars without the all permissions tag keep you in the sandbox. From there you can ask for user approved actions.

4. Use the JNLP services for user approved IO

import javax.jnlp.*;

public class FileOps {
  private FileOpenService fos;
  private FileSaveService fss;

  public FileOps() throws UnavailableServiceException {
    fos = (FileOpenService) ServiceManager.lookup("javax.jnlp.FileOpenService");
    fss = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService");
  }

  public String openText() throws Exception {
    FileContents fc = fos.openFileDialog(null, new String[] {"txt","md"});
    if (fc == null) return null;
    java.io.InputStream in = fc.getInputStream();
    java.util.Scanner s = new java.util.Scanner(in, "UTF-8").useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
  }

  public void saveText(String name, byte[] data) throws Exception {
    java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(data);
    fss.saveFileDialog(null, new String[] {"txt"}, bin, name);
  }
}

No private java.io.File tricks. The user approves the file and Web Start mediates the access. This keeps you inside the sandbox.

5. If you truly need all permissions

– Buy a code signing cert that says your company name.

– Sign every jar and add <all-permissions/> to the JNLP.

– Serve the JNLP and jars over HTTPS. Plain HTTP invites tampering by any proxy in the path.

– Keep a timestamp service in the build script.

– Train your help desk to explain the trust dialog in one sentence: users should see your company name as the publisher and the URL should match your site.

Risks and gotchas

Self signed fatigue. If you ship with self signed certs, users will get used to clicking through a scary dialog. The day a real attack pops up, they will also click through. Spend the money for the cert.

Expired certs without timestamps. Your app runs fine for months then suddenly refuses to launch after the cert expires. The fix is to re sign every jar and redeploy. Save future you by adding a timestamp now.

Mixed signing. A single unsigned jar in the classpath can break trust. The loader will complain or block. Sign them all with the same chain.

Wrong codebase or href. If the JNLP points to a path that does not match where the jars live, updates will get weird. Cache busting will skip files and users will see old code.

HTTP in the chain. Serving the JNLP over HTTP but the jars over HTTPS still lets someone swap the JNLP and point to their jars. Make the entry point HTTPS.

Proxy quirks. Some corporate proxies mangle caching headers. Use Versioned JARs or set Cache-Control: no-cache on the JNLP. Web Start caching is useful when the headers play nice.

Native hooks. If you reach for JNI or print service with custom drivers, you are in all permissions land. Test the trust dialog on every target OS and user account type. UAC on Windows and standard users on Mac behave differently.

Graceful exit

If you remember one thing, let it be this: sign everything and only ask for the power you truly need. Most Web Start apps do great inside the sandbox when they lean on the JNLP services. When you must step out, sign with a real cert, timestamp it, serve it over HTTPS, and keep the JNLP clean.

If you want a starter checklist, here is mine:

– Pick your posture: sandbox with JNLP services or all permissions

– Buy a code signing cert for production

– Sign every jar and add a timestamp

– Serve JNLP and jars over HTTPS

– Avoid mixing signed and unsigned jars

– Test the trust dialog and the update path on a clean machine

Do that and your Web Start story will feel solid to users and friendly to ops. If you get stuck on a prompt or a proxy, ping me with the exact dialog text and your JNLP. Nine times out of ten the fix is in those two files.

Development Practices Software Engineering

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes