SignedJarBuilderpublic class SignedJarBuilder extends Object A Jar file builder with signature support. |
Fields Summary |
---|
private static final String | DIGEST_ALGORITHM | private static final String | DIGEST_ATTR | private static final String | DIGEST_MANIFEST_ATTR | private JarOutputStream | mOutputJar | private PrivateKey | mKey | private X509Certificate | mCertificate | private Manifest | mManifest | private sun.misc.BASE64Encoder | mBase64Encoder | private MessageDigest | mMessageDigest | private byte[] | mBuffer |
Constructors Summary |
---|
public SignedJarBuilder(OutputStream out, PrivateKey key, X509Certificate certificate)Creates a {@link SignedJarBuilder} with a given output stream, and signing information.
If either key or certificate is null then
the archive will not be signed.
mOutputJar = new JarOutputStream(out);
mOutputJar.setLevel(9);
mKey = key;
mCertificate = certificate;
if (mKey != null && mCertificate != null) {
mManifest = new Manifest();
Attributes main = mManifest.getMainAttributes();
main.putValue("Manifest-Version", "1.0");
main.putValue("Created-By", "1.0 (Android)");
mBase64Encoder = new BASE64Encoder();
mMessageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM);
}
|
Methods Summary |
---|
public void | close()Closes the Jar archive by creating the manifest, and signing the archive.
if (mManifest != null) {
// write the manifest to the jar file
mOutputJar.putNextEntry(new JarEntry(JarFile.MANIFEST_NAME));
mManifest.write(mOutputJar);
// CERT.SF
Signature signature = Signature.getInstance("SHA1with" + mKey.getAlgorithm());
signature.initSign(mKey);
mOutputJar.putNextEntry(new JarEntry("META-INF/CERT.SF"));
writeSignatureFile(new SignatureOutputStream(mOutputJar, signature));
// CERT.*
mOutputJar.putNextEntry(new JarEntry("META-INF/CERT." + mKey.getAlgorithm()));
writeSignatureBlock(signature, mCertificate, mKey);
}
mOutputJar.close();
| private void | writeEntry(java.io.InputStream input, java.util.jar.JarEntry entry)Adds an entry to the output jar, and write its content from the {@link InputStream}
// add the entry to the jar archive
mOutputJar.putNextEntry(entry);
// read the content of the entry from the input stream, and write it into the archive.
int count;
while ((count = input.read(mBuffer)) != -1) {
mOutputJar.write(mBuffer, 0, count);
// update the digest
if (mMessageDigest != null) {
mMessageDigest.update(mBuffer, 0, count);
}
}
// close the entry for this file
mOutputJar.closeEntry();
if (mManifest != null) {
// update the manifest for this entry.
Attributes attr = mManifest.getAttributes(entry.getName());
if (attr == null) {
attr = new Attributes();
mManifest.getEntries().put(entry.getName(), attr);
}
attr.putValue(DIGEST_ATTR, mBase64Encoder.encode(mMessageDigest.digest()));
}
| public void | writeFile(java.io.File inputFile, java.lang.String jarPath)Writes a new {@link File} into the archive.
// Get an input stream on the file.
FileInputStream fis = new FileInputStream(inputFile);
try {
// create the zip entry
JarEntry entry = new JarEntry(jarPath);
entry.setTime(inputFile.lastModified());
writeEntry(fis, entry);
} finally {
// close the file stream used to read the file
fis.close();
}
| private void | writeSignatureBlock(java.security.Signature signature, java.security.cert.X509Certificate publicKey, java.security.PrivateKey privateKey)Write the certificate file with a digital signature.
SignerInfo signerInfo = new SignerInfo(
new X500Name(publicKey.getIssuerX500Principal().getName()),
publicKey.getSerialNumber(),
AlgorithmId.get(DIGEST_ALGORITHM),
AlgorithmId.get(privateKey.getAlgorithm()),
signature.sign());
PKCS7 pkcs7 = new PKCS7(
new AlgorithmId[] { AlgorithmId.get(DIGEST_ALGORITHM) },
new ContentInfo(ContentInfo.DATA_OID, null),
new X509Certificate[] { publicKey },
new SignerInfo[] { signerInfo });
pkcs7.encodeSignedData(mOutputJar);
| private void | writeSignatureFile(java.io.OutputStream out)Writes a .SF file with a digest to the manifest.
Manifest sf = new Manifest();
Attributes main = sf.getMainAttributes();
main.putValue("Signature-Version", "1.0");
main.putValue("Created-By", "1.0 (Android)");
BASE64Encoder base64 = new BASE64Encoder();
MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM);
PrintStream print = new PrintStream(
new DigestOutputStream(new ByteArrayOutputStream(), md),
true, "UTF-8");
// Digest of the entire manifest
mManifest.write(print);
print.flush();
main.putValue(DIGEST_MANIFEST_ATTR, base64.encode(md.digest()));
Map<String, Attributes> entries = mManifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
// Digest of the manifest stanza for this entry.
print.print("Name: " + entry.getKey() + "\r\n");
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
}
print.print("\r\n");
print.flush();
Attributes sfAttr = new Attributes();
sfAttr.putValue(DIGEST_ATTR, base64.encode(md.digest()));
sf.getEntries().put(entry.getKey(), sfAttr);
}
sf.write(out);
| public void | writeZip(java.io.InputStream input, com.android.jarutils.SignedJarBuilder$IZipEntryFilter filter)Copies the content of a Jar/Zip archive into the receiver archive.
An optional {@link IZipEntryFilter} allows to selectively choose which files
to copy over.
ZipInputStream zis = new ZipInputStream(input);
try {
// loop on the entries of the intermediary package and put them in the final package.
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
// do not take directories or anything inside a potential META-INF folder.
if (entry.isDirectory() || name.startsWith("META-INF/")) {
continue;
}
// if we have a filter, we check the entry against it
if (filter != null && filter.checkEntry(name) == false) {
continue;
}
JarEntry newEntry;
// Preserve the STORED method of the input entry.
if (entry.getMethod() == JarEntry.STORED) {
newEntry = new JarEntry(entry);
} else {
// Create a new entry so that the compressed len is recomputed.
newEntry = new JarEntry(name);
}
writeEntry(zis, newEntry);
zis.closeEntry();
}
} finally {
zis.close();
}
|
|