// The entry name to be written into attrs String name = null; try { // Find if the digest already exists AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm()); for (Object key: attrs.keySet()) { if (key instanceof Attributes.Name) { String n = ((Attributes.Name)key).toString(); if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { String tmp = n.substring(0, n.length() - 7); if (AlgorithmId.get(tmp).equals(aid)) { name = n; break; } } } } } catch (NoSuchAlgorithmException nsae) { // Ignored. Writing new digest entry. } if (name == null) { name = digests[i].getAlgorithm()+"-Digest"; attrs.putValue(name, base64Digests[i]); update=true; } else { // compare digests, and replace the one in the manifest // if they are different String mfDigest = attrs.getValue(name); if (!mfDigest.equalsIgnoreCase(base64Digests[i])) { attrs.putValue(name, base64Digests[i]); update=true; } } } return update; } /* * Try to load the specified signing mechanism. * The URL class loader is used. */ private ContentSigner loadSigningMechanism(String signerClassName, String signerClassPath) throws Exception { // construct class loader String cpString = null; // make sure env.class.path defaults to dot // do prepends to get correct ordering cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString); cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString); cpString = PathList.appendPath(signerClassPath, cpString); URL[] urls = PathList.pathToURLs(cpString); ClassLoader appClassLoader = new URLClassLoader(urls); // attempt to find signer Class signerClass = appClassLoader.loadClass(signerClassName); // Check that it implements ContentSigner Object signer = signerClass.newInstance(); if (!(signer instanceof ContentSigner)) { MessageFormat form = new MessageFormat( rb.getString("signerClass.is.not.a.signing.mechanism")); Object[] source = {signerClass.getName()}; throw new IllegalArgumentException(form.format(source)); } return (ContentSigner)signer; }} /** * This is a BASE64Encoder that does not insert a default newline at the end of * every output line. This is necessary because java.util.jar does its own * line management (see Manifest.make72Safe()). Inserting additional new lines * can cause line-wrapping problems (see CR 6219522). */class JarBASE64Encoder extends BASE64Encoder { /** * Encode the suffix that ends every output line. */ protected void encodeLineSuffix(OutputStream aStream) throws IOException { }} class SignatureFile { /** SignatureFile */ Manifest sf; /** .SF base name */ String baseName; public SignatureFile(MessageDigest digests[], Manifest mf, ManifestDigester md, String baseName, boolean signManifest) { this.baseName = baseName; String version = System.getProperty("java.version"); String javaVendor = System.getProperty("java.vendor"); sf = new Manifest(); Attributes mattr = sf.getMainAttributes(); BASE64Encoder encoder = new JarBASE64Encoder(); mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); mattr.putValue("Created-By", version + " (" + javaVendor + ")"); if (signManifest) { // sign the whole manifest for (int i=0; i < digests.length; i++) { mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest", encoder.encode(md.manifestDigest(digests[i]))); } } // create digest of the manifest main attributes ManifestDigester.Entry mde = md.get(ManifestDigester.MF_MAIN_ATTRS, false); if (mde != null) { for (int i=0; i < digests.length; i++) { mattr.putValue(digests[i].getAlgorithm() + "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, encoder.encode(mde.digest(digests[i]))); } } else { throw new IllegalStateException ("ManifestDigester failed to create " + "Manifest-Main-Attribute entry"); } /* go through the manifest entries and create the digests */ Map<String,Attributes> entries = sf.getEntries(); Iterator<Map.Entry<String,Attributes>> mit = mf.getEntries().entrySet().iterator(); while(mit.hasNext()) { Map.Entry<String,Attributes> e = mit.next(); String name = e.getKey(); mde = md.get(name, false); if (mde != null) { Attributes attr = new Attributes(); for (int i=0; i < digests.length; i++) { attr.putValue(digests[i].getAlgorithm()+"-Digest", encoder.encode(mde.digest(digests[i]))); } entries.put(name, attr); } } } /** * Writes the SignatureFile to the specified OutputStream. * * @param out the output stream * @exception IOException if an I/O error has occurred */ public void write(OutputStream out) throws IOException { sf.write(out); } /** * get .SF file name */ public String getMetaName() { return "META-INF/"+ baseName + ".SF"; } /** * get base file name */ public String getBaseName() { return baseName; } /* * Generate a signed data block. * If a URL or a certificate (containing a URL) for a Timestamping * Authority is supplied then a signature timestamp is generated and * inserted into the signed data block. * * @param sigalg signature algorithm to use, or null to use default * @param tsaUrl The location of the Timestamping Authority. If null * then no timestamp is requested. * @param tsaCert The certificate for the Timestamping Authority. If null * then no timestamp is requested. * @param signingMechanism The signing mechanism to use. * @param args The command-line arguments to jarsigner. * @param zipFile The original source Zip file. */ public Block generateBlock(PrivateKey privateKey, String sigalg, X509Certificate[] certChain, boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { return new Block(this, privateKey, sigalg, certChain, externalSF, tsaUrl, tsaCert, signingMechanism, args, zipFile); } public static class Block { private byte[] block; private String blockFileName; /* * Construct a new signature block. */ Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, X509Certificate[] certChain, boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { Principal issuerName = certChain[0].getIssuerDN(); if (!(issuerName instanceof X500Name)) { // must extract the original encoded form of DN for subsequent // name comparison checks (converting to a String and back to // an encoded DN could cause the types of String attribute // values to be changed) X509CertInfo tbsCert = new X509CertInfo(certChain[0].getTBSCertificate()); issuerName = (Principal) tbsCert.get(CertificateIssuerName.NAME + "." + CertificateIssuerName.DN_NAME); } BigInteger serial = certChain[0].getSerialNumber(); String signatureAlgorithm; String keyAlgorithm = privateKey.getAlgorithm(); /* * If no signature algorithm was specified, we choose a * default that is compatible with the private key algorithm. */ if (sigalg == null) { if (keyAlgorithm.equalsIgnoreCase("DSA")) signatureAlgorithm = "SHA1withDSA"; else if (keyAlgorithm.equalsIgnoreCase("RSA")) signatureAlgorithm = "SHA256withRSA"; else if (keyAlgorithm.equalsIgnoreCase("EC")) signatureAlgorithm = "SHA256withECDSA"; else throw new RuntimeException("private key is not a DSA or " + "RSA key"); } else { signatureAlgorithm = sigalg; } // check common invalid key/signature algorithm combinations String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH); if ((sigAlgUpperCase.endsWith("WITHRSA") && !keyAlgorithm.equalsIgnoreCase("RSA")) || (sigAlgUpperCase.endsWith("WITHECDSA") && !keyAlgorithm.equalsIgnoreCase("EC")) || (sigAlgUpperCase.endsWith("WITHDSA") && !keyAlgorithm.equalsIgnoreCase("DSA"))) { throw new SignatureException ("private key algorithm is not compatible with signature algorithm"); } blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm; AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm); AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm); Signature sig = Signature.getInstance(signatureAlgorithm); sig.initSign(privateKey); ByteArrayOutputStream baos = new ByteArrayOutputStream(); sfg.write(baos); byte[] content = baos.toByteArray(); sig.update(content); byte[] signature = sig.sign(); // Timestamp the signature and generate the signature block file if (signingMechanism == null) { signingMechanism = new TimestampedSigner(); } URI tsaUri = null; try { if (tsaUrl != null) { tsaUri = new URI(tsaUrl); } } catch (URISyntaxException e) { IOException ioe = new IOException(); ioe.initCause(e); throw ioe; } // Assemble parameters for the signing mechanism ContentSignerParameters params = new JarSignerParameters(args, tsaUri, tsaCert, signature, signatureAlgorithm, certChain, content, zipFile); // Generate the signature block block = signingMechanism.generateSignedData( params, externalSF, (tsaUrl != null || tsaCert != null)); } /* * get block file name. */ public String getMetaName() { return blockFileName; } /** * Writes the block file to the specified OutputStream. * * @param out the output stream * @exception IOException if an I/O error has occurred */ public void write(OutputStream out) throws IOException { out.write(block); } }} /* * This object encapsulates the parameters used to perform content signing. */class JarSignerParameters implements ContentSignerParameters { private String[] args; private URI tsa; private X509Certificate tsaCertificate; private byte[] signature; private String signatureAlgorithm; private X509Certificate[] signerCertificateChain; private byte[] content; private ZipFile source; /** * Create a new object. */ JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, byte[] signature, String signatureAlgorithm, X509Certificate[] signerCertificateChain, byte[] content, ZipFile source) { if (signature == null || signatureAlgorithm == null || signerCertificateChain == null) { throw new NullPointerException(); } this.args = args; this.tsa = tsa; this.tsaCertificate = tsaCertificate; this.signature = signature; this.signatureAlgorithm = signatureAlgorithm; this.signerCertificateChain = signerCertificateChain; this.content = content; this.source = source; } /** * Retrieves the command-line arguments. * * @return The command-line arguments. May be null. */ public String[] getCommandLine() { return args; } /** * Retrieves the identifier for a Timestamping Authority (TSA). * * @return The TSA identifier. May be null. */ public URI getTimestampingAuthority() { return tsa; } /** * Retrieves the certificate for a Timestamping Authority (TSA). * * @return The TSA certificate. May be null. */ public X509Certificate getTimestampingAuthorityCertificate() { return tsaCertificate; } /** * Retrieves the signature. * * @return The non-null signature bytes. */ public byte[] getSignature() { return signature; } /** * Retrieves the name of the signature algorithm. * * @return The non-null string name of the signature algorithm. */ public String getSignatureAlgorithm() { return signatureAlgorithm; } /** * Retrieves the signer's X.509 certificate chain. * * @return The non-null array of X.509 public-key certificates. */ public X509Certificate[] getSignerCertificateChain() { return signerCertificateChain; } /** * Retrieves the content that was signed. * * @return The content bytes. May be null. */ public byte[] getContent() { return content; } /** * Retrieves the original source ZIP file before it was signed. * * @return The original ZIP file. May be null. */ public ZipFile getSource() { return source; }}Lines 2001–2445 of 2445. Previous lines
// The entry name to be written into attrs String name = null; try { // Find if the digest already exists AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm()); for (Object key: attrs.keySet()) { if (key instanceof Attributes.Name) { String n = ((Attributes.Name)key).toString(); if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { String tmp = n.substring(0, n.length() - 7); if (AlgorithmId.get(tmp).equals(aid)) { name = n; break; } } } } } catch (NoSuchAlgorithmException nsae) { // Ignored. Writing new digest entry. } if (name == null) { name = digests[i].getAlgorithm()+"-Digest"; attrs.putValue(name, base64Digests[i]); update=true; } else { // compare digests, and replace the one in the manifest // if they are different String mfDigest = attrs.getValue(name); if (!mfDigest.equalsIgnoreCase(base64Digests[i])) { attrs.putValue(name, base64Digests[i]); update=true; } } } return update; } /* * Try to load the specified signing mechanism. * The URL class loader is used. */ private ContentSigner loadSigningMechanism(String signerClassName, String signerClassPath) throws Exception { // construct class loader String cpString = null; // make sure env.class.path defaults to dot // do prepends to get correct ordering cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString); cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString); cpString = PathList.appendPath(signerClassPath, cpString); URL[] urls = PathList.pathToURLs(cpString); ClassLoader appClassLoader = new URLClassLoader(urls); // attempt to find signer Class signerClass = appClassLoader.loadClass(signerClassName); // Check that it implements ContentSigner Object signer = signerClass.newInstance(); if (!(signer instanceof ContentSigner)) { MessageFormat form = new MessageFormat( rb.getString("signerClass.is.not.a.signing.mechanism")); Object[] source = {signerClass.getName()}; throw new IllegalArgumentException(form.format(source)); } return (ContentSigner)signer; } } /** * This is a BASE64Encoder that does not insert a default newline at the end of * every output line. This is necessary because java.util.jar does its own * line management (see Manifest.make72Safe()). Inserting additional new lines * can cause line-wrapping problems (see CR 6219522). */ class JarBASE64Encoder extends BASE64Encoder { /** * Encode the suffix that ends every output line. */ protected void encodeLineSuffix(OutputStream aStream) throws IOException { } } class SignatureFile { /** SignatureFile */ Manifest sf; /** .SF base name */ String baseName; public SignatureFile(MessageDigest digests[], Manifest mf, ManifestDigester md, String baseName, boolean signManifest) { this.baseName = baseName; String version = System.getProperty("java.version"); String javaVendor = System.getProperty("java.vendor"); sf = new Manifest(); Attributes mattr = sf.getMainAttributes(); BASE64Encoder encoder = new JarBASE64Encoder(); mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); mattr.putValue("Created-By", version + " (" + javaVendor + ")"); if (signManifest) { // sign the whole manifest for (int i=0; i < digests.length; i++) { mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest", encoder.encode(md.manifestDigest(digests[i]))); } } // create digest of the manifest main attributes ManifestDigester.Entry mde = md.get(ManifestDigester.MF_MAIN_ATTRS, false); if (mde != null) { for (int i=0; i < digests.length; i++) { mattr.putValue(digests[i].getAlgorithm() + "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, encoder.encode(mde.digest(digests[i]))); } } else { throw new IllegalStateException ("ManifestDigester failed to create " + "Manifest-Main-Attribute entry"); } /* go through the manifest entries and create the digests */ Map<String,Attributes> entries = sf.getEntries(); Iterator<Map.Entry<String,Attributes>> mit = mf.getEntries().entrySet().iterator(); while(mit.hasNext()) { Map.Entry<String,Attributes> e = mit.next(); String name = e.getKey(); mde = md.get(name, false); if (mde != null) { Attributes attr = new Attributes(); for (int i=0; i < digests.length; i++) { attr.putValue(digests[i].getAlgorithm()+"-Digest", encoder.encode(mde.digest(digests[i]))); } entries.put(name, attr); } } } /** * Writes the SignatureFile to the specified OutputStream. * * @param out the output stream * @exception IOException if an I/O error has occurred */ public void write(OutputStream out) throws IOException { sf.write(out); } /** * get .SF file name */ public String getMetaName() { return "META-INF/"+ baseName + ".SF"; } /** * get base file name */ public String getBaseName() { return baseName; } /* * Generate a signed data block. * If a URL or a certificate (containing a URL) for a Timestamping * Authority is supplied then a signature timestamp is generated and * inserted into the signed data block. * * @param sigalg signature algorithm to use, or null to use default * @param tsaUrl The location of the Timestamping Authority. If null * then no timestamp is requested. * @param tsaCert The certificate for the Timestamping Authority. If null * then no timestamp is requested. * @param signingMechanism The signing mechanism to use. * @param args The command-line arguments to jarsigner. * @param zipFile The original source Zip file. */ public Block generateBlock(PrivateKey privateKey, String sigalg, X509Certificate[] certChain, boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { return new Block(this, privateKey, sigalg, certChain, externalSF, tsaUrl, tsaCert, signingMechanism, args, zipFile); } public static class Block { private byte[] block; private String blockFileName; /* * Construct a new signature block. */ Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, X509Certificate[] certChain, boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { Principal issuerName = certChain[0].getIssuerDN(); if (!(issuerName instanceof X500Name)) { // must extract the original encoded form of DN for subsequent // name comparison checks (converting to a String and back to // an encoded DN could cause the types of String attribute // values to be changed) X509CertInfo tbsCert = new X509CertInfo(certChain[0].getTBSCertificate()); issuerName = (Principal) tbsCert.get(CertificateIssuerName.NAME + "." + CertificateIssuerName.DN_NAME); } BigInteger serial = certChain[0].getSerialNumber(); String signatureAlgorithm; String keyAlgorithm = privateKey.getAlgorithm(); /* * If no signature algorithm was specified, we choose a * default that is compatible with the private key algorithm. */ if (sigalg == null) { if (keyAlgorithm.equalsIgnoreCase("DSA")) signatureAlgorithm = "SHA1withDSA"; else if (keyAlgorithm.equalsIgnoreCase("RSA")) signatureAlgorithm = "SHA256withRSA"; else if (keyAlgorithm.equalsIgnoreCase("EC")) signatureAlgorithm = "SHA256withECDSA"; else throw new RuntimeException("private key is not a DSA or " + "RSA key"); } else { signatureAlgorithm = sigalg; } // check common invalid key/signature algorithm combinations String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH); if ((sigAlgUpperCase.endsWith("WITHRSA") && !keyAlgorithm.equalsIgnoreCase("RSA")) || (sigAlgUpperCase.endsWith("WITHECDSA") && !keyAlgorithm.equalsIgnoreCase("EC")) || (sigAlgUpperCase.endsWith("WITHDSA") && !keyAlgorithm.equalsIgnoreCase("DSA"))) { throw new SignatureException ("private key algorithm is not compatible with signature algorithm"); } blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm; AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm); AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm); Signature sig = Signature.getInstance(signatureAlgorithm); sig.initSign(privateKey); ByteArrayOutputStream baos = new ByteArrayOutputStream(); sfg.write(baos); byte[] content = baos.toByteArray(); sig.update(content); byte[] signature = sig.sign(); // Timestamp the signature and generate the signature block file if (signingMechanism == null) { signingMechanism = new TimestampedSigner(); } URI tsaUri = null; try { if (tsaUrl != null) { tsaUri = new URI(tsaUrl); } } catch (URISyntaxException e) { IOException ioe = new IOException(); ioe.initCause(e); throw ioe; } // Assemble parameters for the signing mechanism ContentSignerParameters params = new JarSignerParameters(args, tsaUri, tsaCert, signature, signatureAlgorithm, certChain, content, zipFile); // Generate the signature block block = signingMechanism.generateSignedData( params, externalSF, (tsaUrl != null || tsaCert != null)); } /* * get block file name. */ public String getMetaName() { return blockFileName; } /** * Writes the block file to the specified OutputStream. * * @param out the output stream * @exception IOException if an I/O error has occurred */ public void write(OutputStream out) throws IOException { out.write(block); } } } /* * This object encapsulates the parameters used to perform content signing. */ class JarSignerParameters implements ContentSignerParameters { private String[] args; private URI tsa; private X509Certificate tsaCertificate; private byte[] signature; private String signatureAlgorithm; private X509Certificate[] signerCertificateChain; private byte[] content; private ZipFile source; /** * Create a new object. */ JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, byte[] signature, String signatureAlgorithm, X509Certificate[] signerCertificateChain, byte[] content, ZipFile source) { if (signature == null || signatureAlgorithm == null || signerCertificateChain == null) { throw new NullPointerException(); } this.args = args; this.tsa = tsa; this.tsaCertificate = tsaCertificate; this.signature = signature; this.signatureAlgorithm = signatureAlgorithm; this.signerCertificateChain = signerCertificateChain; this.content = content; this.source = source; } /** * Retrieves the command-line arguments. * * @return The command-line arguments. May be null. */ public String[] getCommandLine() { return args; } /** * Retrieves the identifier for a Timestamping Authority (TSA). * * @return The TSA identifier. May be null. */ public URI getTimestampingAuthority() { return tsa; } /** * Retrieves the certificate for a Timestamping Authority (TSA). * * @return The TSA certificate. May be null. */ public X509Certificate getTimestampingAuthorityCertificate() { return tsaCertificate; } /** * Retrieves the signature. * * @return The non-null signature bytes. */ public byte[] getSignature() { return signature; } /** * Retrieves the name of the signature algorithm. * * @return The non-null string name of the signature algorithm. */ public String getSignatureAlgorithm() { return signatureAlgorithm; } /** * Retrieves the signer's X.509 certificate chain. * * @return The non-null array of X.509 public-key certificates. */ public X509Certificate[] getSignerCertificateChain() { return signerCertificateChain; } /** * Retrieves the content that was signed. * * @return The content bytes. May be null. */ public byte[] getContent() { return content; } /** * Retrieves the original source ZIP file before it was signed. * * @return The original ZIP file. May be null. */ public ZipFile getSource() { return source; } }