jdk/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
changeset 43701 fe8c324ba97c
parent 41580 cc479488428c
child 43986 717c0cbb60fd
equal deleted inserted replaced
43700:ee6b5bd26bf9 43701:fe8c324ba97c
     1 /*
     1 /*
     2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    26 package sun.security.util;
    26 package sun.security.util;
    27 
    27 
    28 import java.io.ByteArrayInputStream;
    28 import java.io.ByteArrayInputStream;
    29 import java.io.IOException;
    29 import java.io.IOException;
    30 import java.security.CodeSigner;
    30 import java.security.CodeSigner;
    31 import java.security.CryptoPrimitive;
    31 import java.security.GeneralSecurityException;
    32 import java.security.MessageDigest;
    32 import java.security.MessageDigest;
    33 import java.security.NoSuchAlgorithmException;
    33 import java.security.NoSuchAlgorithmException;
    34 import java.security.SignatureException;
    34 import java.security.SignatureException;
       
    35 import java.security.Timestamp;
    35 import java.security.cert.CertPath;
    36 import java.security.cert.CertPath;
    36 import java.security.cert.X509Certificate;
    37 import java.security.cert.X509Certificate;
    37 import java.security.cert.CertificateException;
    38 import java.security.cert.CertificateException;
    38 import java.security.cert.CertificateFactory;
    39 import java.security.cert.CertificateFactory;
    39 import java.util.ArrayList;
    40 import java.util.ArrayList;
    40 import java.util.Base64;
    41 import java.util.Base64;
    41 import java.util.Collections;
       
    42 import java.util.EnumSet;
       
    43 import java.util.HashMap;
    42 import java.util.HashMap;
    44 import java.util.Hashtable;
    43 import java.util.Hashtable;
    45 import java.util.Iterator;
    44 import java.util.Iterator;
    46 import java.util.List;
    45 import java.util.List;
    47 import java.util.Locale;
    46 import java.util.Locale;
    48 import java.util.Map;
    47 import java.util.Map;
    49 import java.util.Set;
       
    50 import java.util.jar.Attributes;
    48 import java.util.jar.Attributes;
    51 import java.util.jar.JarException;
    49 import java.util.jar.JarException;
    52 import java.util.jar.JarFile;
    50 import java.util.jar.JarFile;
    53 import java.util.jar.Manifest;
    51 import java.util.jar.Manifest;
    54 
    52 
    59 public class SignatureFileVerifier {
    57 public class SignatureFileVerifier {
    60 
    58 
    61     /* Are we debugging ? */
    59     /* Are we debugging ? */
    62     private static final Debug debug = Debug.getInstance("jar");
    60     private static final Debug debug = Debug.getInstance("jar");
    63 
    61 
    64     private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET =
       
    65             Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
       
    66 
       
    67     private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
    62     private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
    68             new DisabledAlgorithmConstraints(
    63             new DisabledAlgorithmConstraints(
    69                     DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
    64                     DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
    70 
    65 
    71     private ArrayList<CodeSigner[]> signerCache;
    66     private ArrayList<CodeSigner[]> signerCache;
    94     /* workaround for parsing Netscape jars  */
    89     /* workaround for parsing Netscape jars  */
    95     private boolean workaround = false;
    90     private boolean workaround = false;
    96 
    91 
    97     /* for generating certpath objects */
    92     /* for generating certpath objects */
    98     private CertificateFactory certificateFactory = null;
    93     private CertificateFactory certificateFactory = null;
       
    94 
       
    95     /** Algorithms that have been checked if they are weak. */
       
    96     private Map<String, Boolean> permittedAlgs= new HashMap<>();
       
    97 
       
    98     /** TSA timestamp of signed jar.  The newest timestamp is used.  If there
       
    99      *  was no TSA timestamp used when signed, current time is used ("null").
       
   100      */
       
   101     private Timestamp timestamp = null;
    99 
   102 
   100     /**
   103     /**
   101      * Create the named SignatureFileVerifier.
   104      * Create the named SignatureFileVerifier.
   102      *
   105      *
   103      * @param name the name of the signature block file (.DSA/.RSA/.EC)
   106      * @param name the name of the signature block file (.DSA/.RSA/.EC)
   220         return false;
   223         return false;
   221     }
   224     }
   222 
   225 
   223     /** get digest from cache */
   226     /** get digest from cache */
   224 
   227 
   225     private MessageDigest getDigest(String algorithm) throws SignatureException {
   228     private MessageDigest getDigest(String algorithm)
   226         // check that algorithm is not restricted
   229             throws SignatureException {
   227         if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, algorithm, null)) {
       
   228             SignatureException e =
       
   229                     new SignatureException("SignatureFile check failed. " +
       
   230                             "Disabled algorithm used: " + algorithm);
       
   231             throw e;
       
   232         }
       
   233 
       
   234         if (createdDigests == null)
   230         if (createdDigests == null)
   235             createdDigests = new HashMap<>();
   231             createdDigests = new HashMap<>();
   236 
   232 
   237         MessageDigest digest = createdDigests.get(algorithm);
   233         MessageDigest digest = createdDigests.get(algorithm);
   238 
   234 
   300 
   296 
   301         // make sure we have something to do all this work for...
   297         // make sure we have something to do all this work for...
   302         if (newSigners == null)
   298         if (newSigners == null)
   303             return;
   299             return;
   304 
   300 
       
   301         /*
       
   302          * Look for the latest timestamp in the signature block.  If an entry
       
   303          * has no timestamp, use current time (aka null).
       
   304          */
       
   305         for (CodeSigner s: newSigners) {
       
   306             if (debug != null) {
       
   307                 debug.println("Gathering timestamp for:  " + s.toString());
       
   308             }
       
   309             if (s.getTimestamp() == null) {
       
   310                 timestamp = null;
       
   311                 break;
       
   312             } else if (timestamp == null) {
       
   313                 timestamp = s.getTimestamp();
       
   314             } else {
       
   315                 if (timestamp.getTimestamp().before(
       
   316                         s.getTimestamp().getTimestamp())) {
       
   317                     timestamp = s.getTimestamp();
       
   318                 }
       
   319             }
       
   320         }
       
   321 
   305         Iterator<Map.Entry<String,Attributes>> entries =
   322         Iterator<Map.Entry<String,Attributes>> entries =
   306                                 sf.getEntries().entrySet().iterator();
   323                                 sf.getEntries().entrySet().iterator();
   307 
   324 
   308         // see if we can verify the whole manifest first
   325         // see if we can verify the whole manifest first
   309         boolean manifestSigned = verifyManifestHash(sf, md, manifestDigests);
   326         boolean manifestSigned = verifyManifestHash(sf, md, manifestDigests);
   343         // MANIFEST.MF is always regarded as signed
   360         // MANIFEST.MF is always regarded as signed
   344         updateSigners(newSigners, signers, JarFile.MANIFEST_NAME);
   361         updateSigners(newSigners, signers, JarFile.MANIFEST_NAME);
   345     }
   362     }
   346 
   363 
   347     /**
   364     /**
       
   365      * Check if algorithm is permitted using the permittedAlgs Map.
       
   366      * If the algorithm is not in the map, check against disabled algorithms and
       
   367      * store the result. If the algorithm is in the map use that result.
       
   368      * False is returned for weak algorithm, true for good algorithms.
       
   369      */
       
   370     boolean permittedCheck(String key, String algorithm) {
       
   371         Boolean permitted = permittedAlgs.get(algorithm);
       
   372         if (permitted == null) {
       
   373             try {
       
   374                 JAR_DISABLED_CHECK.permits(algorithm,
       
   375                         new ConstraintsParameters(timestamp));
       
   376             } catch(GeneralSecurityException e) {
       
   377                 permittedAlgs.put(algorithm, Boolean.FALSE);
       
   378                 permittedAlgs.put(key.toUpperCase(), Boolean.FALSE);
       
   379                 if (debug != null) {
       
   380                     if (e.getMessage() != null) {
       
   381                         debug.println(key + ":  " + e.getMessage());
       
   382                     } else {
       
   383                         debug.println(key + ":  " + algorithm +
       
   384                                 " was disabled, no exception msg given.");
       
   385                         e.printStackTrace();
       
   386                     }
       
   387                 }
       
   388                 return false;
       
   389             }
       
   390 
       
   391             permittedAlgs.put(algorithm, Boolean.TRUE);
       
   392             return true;
       
   393         }
       
   394 
       
   395         // Algorithm has already been checked, return the value from map.
       
   396         return permitted.booleanValue();
       
   397     }
       
   398 
       
   399     /**
       
   400      * With a given header (*-DIGEST*), return a string that lists all the
       
   401      * algorithms associated with the header.
       
   402      * If there are none, return "Unknown Algorithm".
       
   403      */
       
   404     String getWeakAlgorithms(String header) {
       
   405         String w = "";
       
   406         try {
       
   407             for (String key : permittedAlgs.keySet()) {
       
   408                 if (key.endsWith(header)) {
       
   409                     w += key.substring(0, key.length() - header.length()) + " ";
       
   410                 }
       
   411             }
       
   412         } catch (RuntimeException e) {
       
   413             w = "Unknown Algorithm(s).  Error processing " + header + ".  " +
       
   414                     e.getMessage();
       
   415         }
       
   416 
       
   417         // This means we have an error in finding weak algorithms, run in
       
   418         // debug mode to see permittedAlgs map's values.
       
   419         if (w.length() == 0) {
       
   420             return "Unknown Algorithm(s)";
       
   421         }
       
   422 
       
   423         return w;
       
   424     }
       
   425 
       
   426     /**
   348      * See if the whole manifest was signed.
   427      * See if the whole manifest was signed.
   349      */
   428      */
   350     private boolean verifyManifestHash(Manifest sf,
   429     private boolean verifyManifestHash(Manifest sf,
   351                                        ManifestDigester md,
   430                                        ManifestDigester md,
   352                                        List<Object> manifestDigests)
   431                                        List<Object> manifestDigests)
   353          throws IOException, SignatureException
   432          throws IOException, SignatureException
   354     {
   433     {
   355         Attributes mattr = sf.getMainAttributes();
   434         Attributes mattr = sf.getMainAttributes();
   356         boolean manifestSigned = false;
   435         boolean manifestSigned = false;
       
   436         boolean weakAlgs = true;
   357 
   437 
   358         // go through all the attributes and process *-Digest-Manifest entries
   438         // go through all the attributes and process *-Digest-Manifest entries
   359         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
   439         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
   360 
   440 
   361             String key = se.getKey().toString();
   441             String key = se.getKey().toString();
   362 
   442 
   363             if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) {
   443             if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) {
   364                 // 16 is length of "-Digest-Manifest"
   444                 // 16 is length of "-Digest-Manifest"
   365                 String algorithm = key.substring(0, key.length()-16);
   445                 String algorithm = key.substring(0, key.length()-16);
       
   446 
       
   447                 // Check if this algorithm is permitted, skip if false.
       
   448                 if (!permittedCheck(key, algorithm)) {
       
   449                     continue;
       
   450                 }
       
   451 
       
   452                 // A non-weak algorithm was used, any weak algorithms found do
       
   453                 // not need to be reported.
       
   454                 weakAlgs = false;
   366 
   455 
   367                 manifestDigests.add(key);
   456                 manifestDigests.add(key);
   368                 manifestDigests.add(se.getValue());
   457                 manifestDigests.add(se.getValue());
   369                 MessageDigest digest = getDigest(algorithm);
   458                 MessageDigest digest = getDigest(algorithm);
   370                 if (digest != null) {
   459                 if (digest != null) {
   371                     byte[] computedHash = md.manifestDigest(digest);
   460                     byte[] computedHash = md.manifestDigest(digest);
   372                     byte[] expectedHash =
   461                     byte[] expectedHash =
   373                         Base64.getMimeDecoder().decode((String)se.getValue());
   462                         Base64.getMimeDecoder().decode((String)se.getValue());
   374 
   463 
   375                     if (debug != null) {
   464                     if (debug != null) {
   376                      debug.println("Signature File: Manifest digest " +
   465                         debug.println("Signature File: Manifest digest " +
   377                                           digest.getAlgorithm());
   466                                 algorithm);
   378                      debug.println( "  sigfile  " + toHex(expectedHash));
   467                         debug.println( "  sigfile  " + toHex(expectedHash));
   379                      debug.println( "  computed " + toHex(computedHash));
   468                         debug.println( "  computed " + toHex(computedHash));
   380                      debug.println();
   469                         debug.println();
   381                     }
   470                     }
   382 
   471 
   383                     if (MessageDigest.isEqual(computedHash,
   472                     if (MessageDigest.isEqual(computedHash, expectedHash)) {
   384                                               expectedHash)) {
       
   385                         manifestSigned = true;
   473                         manifestSigned = true;
   386                     } else {
   474                     } else {
   387                         //XXX: we will continue and verify each section
   475                         //XXX: we will continue and verify each section
   388                     }
   476                     }
   389                 }
   477                 }
   390             }
   478             }
   391         }
   479         }
       
   480 
       
   481         if (debug != null) {
       
   482             debug.println("PermittedAlgs mapping: ");
       
   483             for (String key : permittedAlgs.keySet()) {
       
   484                 debug.println(key + " : " +
       
   485                         permittedAlgs.get(key).toString());
       
   486             }
       
   487         }
       
   488 
       
   489         // If there were only weak algorithms used, throw an exception.
       
   490         if (weakAlgs) {
       
   491             String weakAlgorithms = getWeakAlgorithms("-DIGEST-MANIFEST");
       
   492             throw new SignatureException("Manifest hash check failed " +
       
   493                     "(DIGEST-MANIFEST). Disabled algorithm(s) used: " +
       
   494                     weakAlgorithms);
       
   495         }
   392         return manifestSigned;
   496         return manifestSigned;
   393     }
   497     }
   394 
   498 
   395     private boolean verifyManifestMainAttrs(Manifest sf,
   499     private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md)
   396                                         ManifestDigester md)
       
   397          throws IOException, SignatureException
   500          throws IOException, SignatureException
   398     {
   501     {
   399         Attributes mattr = sf.getMainAttributes();
   502         Attributes mattr = sf.getMainAttributes();
   400         boolean attrsVerified = true;
   503         boolean attrsVerified = true;
       
   504         boolean weakAlgs = true;
   401 
   505 
   402         // go through all the attributes and process
   506         // go through all the attributes and process
   403         // digest entries for the manifest main attributes
   507         // digest entries for the manifest main attributes
   404         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
   508         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
   405             String key = se.getKey().toString();
   509             String key = se.getKey().toString();
   406 
   510 
   407             if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) {
   511             if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) {
   408                 String algorithm =
   512                 String algorithm =
   409                         key.substring(0, key.length() - ATTR_DIGEST.length());
   513                         key.substring(0, key.length() - ATTR_DIGEST.length());
       
   514 
       
   515                 // Check if this algorithm is permitted, skip if false.
       
   516                 if (!permittedCheck(key, algorithm)) {
       
   517                     continue;
       
   518                 }
       
   519 
       
   520                 // A non-weak algorithm was used, any weak algorithms found do
       
   521                 // not need to be reported.
       
   522                 weakAlgs = false;
   410 
   523 
   411                 MessageDigest digest = getDigest(algorithm);
   524                 MessageDigest digest = getDigest(algorithm);
   412                 if (digest != null) {
   525                 if (digest != null) {
   413                     ManifestDigester.Entry mde =
   526                     ManifestDigester.Entry mde =
   414                         md.get(ManifestDigester.MF_MAIN_ATTRS, false);
   527                         md.get(ManifestDigester.MF_MAIN_ATTRS, false);
   423                      debug.println( "  sigfile  " + toHex(expectedHash));
   536                      debug.println( "  sigfile  " + toHex(expectedHash));
   424                      debug.println( "  computed " + toHex(computedHash));
   537                      debug.println( "  computed " + toHex(computedHash));
   425                      debug.println();
   538                      debug.println();
   426                     }
   539                     }
   427 
   540 
   428                     if (MessageDigest.isEqual(computedHash,
   541                     if (MessageDigest.isEqual(computedHash, expectedHash)) {
   429                                               expectedHash)) {
       
   430                         // good
   542                         // good
   431                     } else {
   543                     } else {
   432                         // we will *not* continue and verify each section
   544                         // we will *not* continue and verify each section
   433                         attrsVerified = false;
   545                         attrsVerified = false;
   434                         if (debug != null) {
   546                         if (debug != null) {
   440                     }
   552                     }
   441                 }
   553                 }
   442             }
   554             }
   443         }
   555         }
   444 
   556 
       
   557         if (debug != null) {
       
   558             debug.println("PermittedAlgs mapping: ");
       
   559             for (String key : permittedAlgs.keySet()) {
       
   560                 debug.println(key + " : " +
       
   561                         permittedAlgs.get(key).toString());
       
   562             }
       
   563         }
       
   564 
       
   565         // If there were only weak algorithms used, throw an exception.
       
   566         if (weakAlgs) {
       
   567             String weakAlgorithms = getWeakAlgorithms("-DIGEST-" +
       
   568                     ManifestDigester.MF_MAIN_ATTRS);
       
   569             throw new SignatureException("Manifest Main Attribute check " +
       
   570                     "failed (DIGEST-" + ManifestDigester.MF_MAIN_ATTRS +
       
   571                     "). " + "Disabled algorithm(s) used: " + weakAlgorithms);
       
   572         }
       
   573 
   445         // this method returns 'true' if either:
   574         // this method returns 'true' if either:
   446         //      . manifest main attributes were not signed, or
   575         //      . manifest main attributes were not signed, or
   447         //      . manifest main attributes were signed and verified
   576         //      . manifest main attributes were signed and verified
   448         return attrsVerified;
   577         return attrsVerified;
   449     }
   578     }
   462                                   ManifestDigester md)
   591                                   ManifestDigester md)
   463          throws IOException, SignatureException
   592          throws IOException, SignatureException
   464     {
   593     {
   465         boolean oneDigestVerified = false;
   594         boolean oneDigestVerified = false;
   466         ManifestDigester.Entry mde = md.get(name,block.isOldStyle());
   595         ManifestDigester.Entry mde = md.get(name,block.isOldStyle());
       
   596         boolean weakAlgs = true;
   467 
   597 
   468         if (mde == null) {
   598         if (mde == null) {
   469             throw new SecurityException(
   599             throw new SecurityException(
   470                   "no manifest section for signature file entry "+name);
   600                   "no manifest section for signature file entry "+name);
   471         }
   601         }
   472 
   602 
   473         if (sfAttr != null) {
   603         if (sfAttr != null) {
   474 
       
   475             //sun.security.util.HexDumpEncoder hex = new sun.security.util.HexDumpEncoder();
   604             //sun.security.util.HexDumpEncoder hex = new sun.security.util.HexDumpEncoder();
   476             //hex.encodeBuffer(data, System.out);
   605             //hex.encodeBuffer(data, System.out);
   477 
   606 
   478             // go through all the attributes and process *-Digest entries
   607             // go through all the attributes and process *-Digest entries
   479             for (Map.Entry<Object,Object> se : sfAttr.entrySet()) {
   608             for (Map.Entry<Object,Object> se : sfAttr.entrySet()) {
   480                 String key = se.getKey().toString();
   609                 String key = se.getKey().toString();
   481 
   610 
   482                 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
   611                 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
   483                     // 7 is length of "-Digest"
   612                     // 7 is length of "-Digest"
   484                     String algorithm = key.substring(0, key.length()-7);
   613                     String algorithm = key.substring(0, key.length()-7);
       
   614 
       
   615                     // Check if this algorithm is permitted, skip if false.
       
   616                     if (!permittedCheck(key, algorithm)) {
       
   617                         continue;
       
   618                     }
       
   619 
       
   620                     // A non-weak algorithm was used, any weak algorithms found do
       
   621                     // not need to be reported.
       
   622                     weakAlgs = false;
   485 
   623 
   486                     MessageDigest digest = getDigest(algorithm);
   624                     MessageDigest digest = getDigest(algorithm);
   487 
   625 
   488                     if (digest != null) {
   626                     if (digest != null) {
   489                         boolean ok = false;
   627                         boolean ok = false;
   530                         }
   668                         }
   531                     }
   669                     }
   532                 }
   670                 }
   533             }
   671             }
   534         }
   672         }
       
   673 
       
   674         if (debug != null) {
       
   675             debug.println("PermittedAlgs mapping: ");
       
   676             for (String key : permittedAlgs.keySet()) {
       
   677                 debug.println(key + " : " +
       
   678                         permittedAlgs.get(key).toString());
       
   679             }
       
   680         }
       
   681 
       
   682         // If there were only weak algorithms used, throw an exception.
       
   683         if (weakAlgs) {
       
   684             String weakAlgorithms = getWeakAlgorithms("DIGEST");
       
   685             throw new SignatureException("Manifest Main Attribute check " +
       
   686                     "failed (DIGEST). " + "Disabled algorithm(s) used: " +
       
   687                     weakAlgorithms);
       
   688         }
       
   689 
   535         return oneDigestVerified;
   690         return oneDigestVerified;
   536     }
   691     }
   537 
   692 
   538     /**
   693     /**
   539      * Given the PKCS7 block and SignerInfo[], create an array of
   694      * Given the PKCS7 block and SignerInfo[], create an array of