Java Store and license management
The Java Store that was previewed during Java One earlier this year is getting ready to release support for paid applications. Developers who are getting ready to publish apps to the store should be aware of the license rights management facility. Here are the requirements in a nutshell:
- Is transparent to users
- Ensures users stay within their usage rights
- Ensures that simple script based attacks to remove the rights management code aren't feasible
- Eliminates the need to have an online connection when the application is used
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Properties;
/**
* A sample application demonstrating how to decode and verify the Java Store rights object.
*
*/
public class App {
public static void main(String[] args) throws Exception {
String sample =
// blob
"...⁞";
// The rights object is provided as a single string containing 3 sections separated by carriage
// returns. First is the blob describing the rights, second is the signature for the blob and
// third is the certificate matching the private key used to generate the signature.
String[] BASE64pieces = sample.split("\n");
if (3 != BASE64pieces.length) {
throw new IllegalArgumentException("invalid signed blob.");
}
byte[][] pieces = {null, null, null};
// each piece is encoded as a separate BASE64 block.
for (int eachPiece = 0; eachPiece < BASE64pieces.length; eachPiece++) {
pieces[eachPiece] = decodeBase64(BASE64pieces[eachPiece]);
}
// Read in the certificate.
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(
pieces[2]));
// Verify the signature in piece[1] using the certificate from piece[2] against the blob from piece[0]
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(certificate);
signature.update(pieces[0]);
if (!signature.verify(pieces[1])) {
throw new IllegalArgumentException("Signature doesn't match.");
}
// Read a Properties map from the rights blob.
Properties rights = new Properties();
rights.load(new ByteArrayInputStream(pieces[0]));
// extract some values from the properties map
// The ID of the product to which the rights are applied.
int productId = Integer.parseInt(rights.getProperty("productId", "-1"));
rights.remove("productId");
// The ID of the user holding the rights.
int userId = Integer.parseInt(rights.getProperty("userId", "-1"));
rights.remove("userId");
// The ID of the device on which the application is running.
String deviceId = rights.getProperty("deviceId", "");
rights.remove("deviceId");
// Timestamp at which this rights descriptor was generated in milliseconds since Midnight Jan 1, 1970 UTC ("the epoch").
long timestamp = Long.parseLong(rights.getProperty("timestamp"));
rights.remove("timestamp");
// Print out the core properties
System.out.printf("product : %d user : %d device : '%s' timestamp : %d \n", productId, userId, deviceId, timestamp);
// Print out properties for each right. Remaining keys should all be single right descriptors.
for (Map.Entry