Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java
38830 views
/*1* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs;2627import java.io.OutputStream;28import java.io.IOException;29import java.math.BigInteger;30import java.security.cert.CertPathValidatorException;31import java.security.cert.CertificateException;32import java.security.cert.CertificateFactory;33import java.security.cert.CertPath;34import java.security.cert.X509Certificate;35import java.security.*;36import java.util.ArrayList;37import java.util.Collections;38import java.util.Date;39import java.util.HashMap;40import java.util.HashSet;41import java.util.Map;42import java.util.Set;4344import sun.misc.HexDumpEncoder;45import sun.security.timestamp.TimestampToken;46import sun.security.util.Debug;47import sun.security.util.DerEncoder;48import sun.security.util.DerInputStream;49import sun.security.util.DerOutputStream;50import sun.security.util.DerValue;51import sun.security.util.DisabledAlgorithmConstraints;52import sun.security.util.JarConstraintsParameters;53import sun.security.util.KeyUtil;54import sun.security.util.ObjectIdentifier;55import sun.security.util.SignatureUtil;56import sun.security.x509.AlgorithmId;57import sun.security.x509.X500Name;58import sun.security.x509.KeyUsageExtension;5960/**61* A SignerInfo, as defined in PKCS#7's signedData type.62*63* @author Benjamin Renaud64*/65public class SignerInfo implements DerEncoder {6667private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =68DisabledAlgorithmConstraints.jarConstraints();6970BigInteger version;71X500Name issuerName;72BigInteger certificateSerialNumber;73AlgorithmId digestAlgorithmId;74AlgorithmId digestEncryptionAlgorithmId;75byte[] encryptedDigest;76Timestamp timestamp;77private boolean hasTimestamp = true;78private static final Debug debug = Debug.getInstance("jar");7980PKCS9Attributes authenticatedAttributes;81PKCS9Attributes unauthenticatedAttributes;8283/**84* A map containing the algorithms in this SignerInfo. This is used to85* avoid checking algorithms to see if they are disabled more than once.86* The key is the AlgorithmId of the algorithm, and the value is the name of87* the field or attribute.88*/89private Map<AlgorithmId, String> algorithms = new HashMap<>();9091public SignerInfo(X500Name issuerName,92BigInteger serial,93AlgorithmId digestAlgorithmId,94AlgorithmId digestEncryptionAlgorithmId,95byte[] encryptedDigest) {96this.version = BigInteger.ONE;97this.issuerName = issuerName;98this.certificateSerialNumber = serial;99this.digestAlgorithmId = digestAlgorithmId;100this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;101this.encryptedDigest = encryptedDigest;102}103104public SignerInfo(X500Name issuerName,105BigInteger serial,106AlgorithmId digestAlgorithmId,107PKCS9Attributes authenticatedAttributes,108AlgorithmId digestEncryptionAlgorithmId,109byte[] encryptedDigest,110PKCS9Attributes unauthenticatedAttributes) {111this.version = BigInteger.ONE;112this.issuerName = issuerName;113this.certificateSerialNumber = serial;114this.digestAlgorithmId = digestAlgorithmId;115this.authenticatedAttributes = authenticatedAttributes;116this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;117this.encryptedDigest = encryptedDigest;118this.unauthenticatedAttributes = unauthenticatedAttributes;119}120121/**122* Parses a PKCS#7 signer info.123*/124public SignerInfo(DerInputStream derin)125throws IOException, ParsingException126{127this(derin, false);128}129130/**131* Parses a PKCS#7 signer info.132*133* <p>This constructor is used only for backwards compatibility with134* PKCS#7 blocks that were generated using JDK1.1.x.135*136* @param derin the ASN.1 encoding of the signer info.137* @param oldStyle flag indicating whether or not the given signer info138* is encoded according to JDK1.1.x.139*/140public SignerInfo(DerInputStream derin, boolean oldStyle)141throws IOException, ParsingException142{143// version144version = derin.getBigInteger();145146// issuerAndSerialNumber147DerValue[] issuerAndSerialNumber = derin.getSequence(2);148if (issuerAndSerialNumber.length != 2) {149throw new ParsingException("Invalid length for IssuerAndSerialNumber");150}151byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();152issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,153issuerBytes));154certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger();155156// digestAlgorithmId157DerValue tmp = derin.getDerValue();158159digestAlgorithmId = AlgorithmId.parse(tmp);160161// authenticatedAttributes162if (oldStyle) {163// In JDK1.1.x, the authenticatedAttributes are always present,164// encoded as an empty Set (Set of length zero)165derin.getSet(0);166} else {167// check if set of auth attributes (implicit tag) is provided168// (auth attributes are OPTIONAL)169if ((byte)(derin.peekByte()) == (byte)0xA0) {170authenticatedAttributes = new PKCS9Attributes(derin);171}172}173174// digestEncryptionAlgorithmId - little RSA naming scheme -175// signature == encryption...176tmp = derin.getDerValue();177178digestEncryptionAlgorithmId = AlgorithmId.parse(tmp);179180// encryptedDigest181encryptedDigest = derin.getOctetString();182183// unauthenticatedAttributes184if (oldStyle) {185// In JDK1.1.x, the unauthenticatedAttributes are always present,186// encoded as an empty Set (Set of length zero)187derin.getSet(0);188} else {189// check if set of unauth attributes (implicit tag) is provided190// (unauth attributes are OPTIONAL)191if (derin.available() != 0192&& (byte)(derin.peekByte()) == (byte)0xA1) {193unauthenticatedAttributes =194new PKCS9Attributes(derin, true);// ignore unsupported attrs195}196}197198// all done199if (derin.available() != 0) {200throw new ParsingException("extra data at the end");201}202}203204public void encode(DerOutputStream out) throws IOException {205206derEncode(out);207}208209/**210* DER encode this object onto an output stream.211* Implements the {@code DerEncoder} interface.212*213* @param out214* the output stream on which to write the DER encoding.215*216* @exception IOException on encoding error.217*/218public void derEncode(OutputStream out) throws IOException {219DerOutputStream seq = new DerOutputStream();220seq.putInteger(version);221DerOutputStream issuerAndSerialNumber = new DerOutputStream();222issuerName.encode(issuerAndSerialNumber);223issuerAndSerialNumber.putInteger(certificateSerialNumber);224seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);225226digestAlgorithmId.encode(seq);227228// encode authenticated attributes if there are any229if (authenticatedAttributes != null)230authenticatedAttributes.encode((byte)0xA0, seq);231232digestEncryptionAlgorithmId.encode(seq);233234seq.putOctetString(encryptedDigest);235236// encode unauthenticated attributes if there are any237if (unauthenticatedAttributes != null)238unauthenticatedAttributes.encode((byte)0xA1, seq);239240DerOutputStream tmp = new DerOutputStream();241tmp.write(DerValue.tag_Sequence, seq);242243out.write(tmp.toByteArray());244}245246247248/*249* Returns the (user) certificate pertaining to this SignerInfo.250*/251public X509Certificate getCertificate(PKCS7 block)252throws IOException253{254return block.getCertificate(certificateSerialNumber, issuerName);255}256257/*258* Returns the certificate chain pertaining to this SignerInfo.259*/260public ArrayList<X509Certificate> getCertificateChain(PKCS7 block)261throws IOException262{263X509Certificate userCert;264userCert = block.getCertificate(certificateSerialNumber, issuerName);265if (userCert == null)266return null;267268ArrayList<X509Certificate> certList = new ArrayList<>();269certList.add(userCert);270271X509Certificate[] pkcsCerts = block.getCertificates();272if (pkcsCerts == null273|| userCert.getSubjectDN().equals(userCert.getIssuerDN())) {274return certList;275}276277Principal issuer = userCert.getIssuerDN();278int start = 0;279while (true) {280boolean match = false;281int i = start;282while (i < pkcsCerts.length) {283if (issuer.equals(pkcsCerts[i].getSubjectDN())) {284// next cert in chain found285certList.add(pkcsCerts[i]);286// if selected cert is self-signed, we're done287// constructing the chain288if (pkcsCerts[i].getSubjectDN().equals(289pkcsCerts[i].getIssuerDN())) {290start = pkcsCerts.length;291} else {292issuer = pkcsCerts[i].getIssuerDN();293X509Certificate tmpCert = pkcsCerts[start];294pkcsCerts[start] = pkcsCerts[i];295pkcsCerts[i] = tmpCert;296start++;297}298match = true;299break;300} else {301i++;302}303}304if (!match)305break;306}307308return certList;309}310311/* Returns null if verify fails, this signerInfo if312verify succeeds. */313SignerInfo verify(PKCS7 block, byte[] data)314throws NoSuchAlgorithmException, SignatureException {315316try {317Timestamp timestamp = getTimestamp();318319ContentInfo content = block.getContentInfo();320if (data == null) {321data = content.getContentBytes();322}323324String digestAlgName = digestAlgorithmId.getName();325algorithms.put(digestAlgorithmId, "SignerInfo digestAlgorithm field");326327byte[] dataSigned;328329// if there are authenticate attributes, get the message330// digest and compare it with the digest of data331if (authenticatedAttributes == null) {332dataSigned = data;333} else {334335// first, check content type336ObjectIdentifier contentType = (ObjectIdentifier)337authenticatedAttributes.getAttributeValue(338PKCS9Attribute.CONTENT_TYPE_OID);339if (contentType == null ||340!contentType.equals((Object)content.contentType))341return null; // contentType does not match, bad SignerInfo342343// now, check message digest344byte[] messageDigest = (byte[])345authenticatedAttributes.getAttributeValue(346PKCS9Attribute.MESSAGE_DIGEST_OID);347348if (messageDigest == null) // fail if there is no message digest349return null;350351MessageDigest md = MessageDigest.getInstance(digestAlgName);352byte[] computedMessageDigest = md.digest(data);353354if (!MessageDigest.isEqual(messageDigest, computedMessageDigest)) {355return null;356}357358// message digest attribute matched359// digest of original data360361// the data actually signed is the DER encoding of362// the authenticated attributes (tagged with363// the "SET OF" tag, not 0xA0).364dataSigned = authenticatedAttributes.getDerEncoding();365}366367// put together digest algorithm and encryption algorithm368// to form signing algorithm369String encryptionAlgName =370getDigestEncryptionAlgorithmId().getName();371372// Workaround: sometimes the encryptionAlgname is actually373// a signature name374String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgName);375if (tmp != null) encryptionAlgName = tmp;376String sigAlgName = AlgorithmId.makeSigAlg(377digestAlgName, encryptionAlgName);378try {379ObjectIdentifier oid = AlgorithmId.get(sigAlgName).getOID();380AlgorithmId sigAlgId =381new AlgorithmId(oid,382digestEncryptionAlgorithmId.getParameters());383algorithms.put(sigAlgId,384"SignerInfo digestEncryptionAlgorithm field");385} catch (NoSuchAlgorithmException ignore) {386}387388X509Certificate cert = getCertificate(block);389if (cert == null) {390return null;391}392PublicKey key = cert.getPublicKey();393394if (cert.hasUnsupportedCriticalExtension()) {395throw new SignatureException("Certificate has unsupported "396+ "critical extension(s)");397}398399// Make sure that if the usage of the key in the certificate is400// restricted, it can be used for digital signatures.401// XXX We may want to check for additional extensions in the402// future.403boolean[] keyUsageBits = cert.getKeyUsage();404if (keyUsageBits != null) {405KeyUsageExtension keyUsage;406try {407// We don't care whether or not this extension was marked408// critical in the certificate.409// We're interested only in its value (i.e., the bits set)410// and treat the extension as critical.411keyUsage = new KeyUsageExtension(keyUsageBits);412} catch (IOException ioe) {413throw new SignatureException("Failed to parse keyUsage "414+ "extension");415}416417boolean digSigAllowed = keyUsage.get(418KeyUsageExtension.DIGITAL_SIGNATURE).booleanValue();419420boolean nonRepuAllowed = keyUsage.get(421KeyUsageExtension.NON_REPUDIATION).booleanValue();422423if (!digSigAllowed && !nonRepuAllowed) {424throw new SignatureException("Key usage restricted: "425+ "cannot be used for "426+ "digital signatures");427}428}429430Signature sig = Signature.getInstance(sigAlgName);431432AlgorithmParameters ap =433digestEncryptionAlgorithmId.getParameters();434try {435SignatureUtil.initVerifyWithParam(sig, key,436SignatureUtil.getParamSpec(sigAlgName, ap));437} catch (ProviderException | InvalidAlgorithmParameterException |438InvalidKeyException e) {439throw new SignatureException(e.getMessage(), e);440}441442sig.update(dataSigned);443if (sig.verify(encryptedDigest)) {444return this;445}446} catch (IOException | CertificateException e) {447throw new SignatureException("Error verifying signature", e);448}449return null;450}451452/* Verify the content of the pkcs7 block. */453SignerInfo verify(PKCS7 block)454throws NoSuchAlgorithmException, SignatureException {455return verify(block, null);456}457458public BigInteger getVersion() {459return version;460}461462public X500Name getIssuerName() {463return issuerName;464}465466public BigInteger getCertificateSerialNumber() {467return certificateSerialNumber;468}469470public AlgorithmId getDigestAlgorithmId() {471return digestAlgorithmId;472}473474public PKCS9Attributes getAuthenticatedAttributes() {475return authenticatedAttributes;476}477478public AlgorithmId getDigestEncryptionAlgorithmId() {479return digestEncryptionAlgorithmId;480}481482public byte[] getEncryptedDigest() {483return encryptedDigest;484}485486public PKCS9Attributes getUnauthenticatedAttributes() {487return unauthenticatedAttributes;488}489490/**491* Returns the timestamp PKCS7 data unverified.492* @return a PKCS7 object493*/494public PKCS7 getTsToken() throws IOException {495if (unauthenticatedAttributes == null) {496return null;497}498PKCS9Attribute tsTokenAttr =499unauthenticatedAttributes.getAttribute(500PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);501if (tsTokenAttr == null) {502return null;503}504return new PKCS7((byte[])tsTokenAttr.getValue());505}506507/*508* Extracts a timestamp from a PKCS7 SignerInfo.509*510* Examines the signer's unsigned attributes for a511* {@code signatureTimestampToken} attribute. If present,512* then it is parsed to extract the date and time at which the513* timestamp was generated.514*515* @param info A signer information element of a PKCS 7 block.516*517* @return A timestamp token or null if none is present.518* @throws IOException if an error is encountered while parsing the519* PKCS7 data.520* @throws NoSuchAlgorithmException if an error is encountered while521* verifying the PKCS7 object.522* @throws SignatureException if an error is encountered while523* verifying the PKCS7 object.524* @throws CertificateException if an error is encountered while generating525* the TSA's certpath.526*/527public Timestamp getTimestamp()528throws IOException, NoSuchAlgorithmException, SignatureException,529CertificateException530{531if (timestamp != null || !hasTimestamp)532return timestamp;533534PKCS7 tsToken = getTsToken();535if (tsToken == null) {536hasTimestamp = false;537return null;538}539540// Extract the content (an encoded timestamp token info)541byte[] encTsTokenInfo = tsToken.getContentInfo().getData();542// Extract the signer (the Timestamping Authority)543// while verifying the content544SignerInfo[] tsa = tsToken.verify(encTsTokenInfo);545if (tsa == null || tsa.length == 0) {546throw new SignatureException("Unable to verify timestamp");547}548// Expect only one signer549ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken);550CertificateFactory cf = CertificateFactory.getInstance("X.509");551CertPath tsaChain = cf.generateCertPath(chain);552// Create a timestamp token info object553TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);554// Check that the signature timestamp applies to this signature555verifyTimestamp(tsTokenInfo);556algorithms.putAll(tsa[0].algorithms);557// Create a timestamp object558timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain);559return timestamp;560}561562/*563* Check that the signature timestamp applies to this signature.564* Match the hash present in the signature timestamp token against the hash565* of this signature.566*/567private void verifyTimestamp(TimestampToken token)568throws NoSuchAlgorithmException, SignatureException {569570AlgorithmId digestAlgId = token.getHashAlgorithm();571algorithms.put(digestAlgId, "TimestampToken digestAlgorithm field");572573MessageDigest md = MessageDigest.getInstance(digestAlgId.getName());574575if (!MessageDigest.isEqual(token.getHashedMessage(),576md.digest(encryptedDigest))) {577578throw new SignatureException("Signature timestamp (#" +579token.getSerialNumber() + ") generated on " + token.getDate() +580" is inapplicable");581}582583if (debug != null) {584debug.println();585debug.println("Detected signature timestamp (#" +586token.getSerialNumber() + ") generated on " + token.getDate());587debug.println();588}589}590591public String toString() {592HexDumpEncoder hexDump = new HexDumpEncoder();593594String out = "";595596out += "Signer Info for (issuer): " + issuerName + "\n";597out += "\tversion: " + Debug.toHexString(version) + "\n";598out += "\tcertificateSerialNumber: " +599Debug.toHexString(certificateSerialNumber) + "\n";600out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";601if (authenticatedAttributes != null) {602out += "\tauthenticatedAttributes: " + authenticatedAttributes +603"\n";604}605out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId +606"\n";607608out += "\tencryptedDigest: " + "\n" +609hexDump.encodeBuffer(encryptedDigest) + "\n";610if (unauthenticatedAttributes != null) {611out += "\tunauthenticatedAttributes: " +612unauthenticatedAttributes + "\n";613}614return out;615}616617/**618* Verify all of the algorithms in the array of SignerInfos against the619* constraints in the jdk.jar.disabledAlgorithms security property.620*621* @param infos array of SignerInfos622* @param params constraint parameters623* @param name the name of the signer's PKCS7 file624* @return a set of algorithms that passed the checks and are not disabled625*/626public static Set<String> verifyAlgorithms(SignerInfo[] infos,627JarConstraintsParameters params, String name) throws SignatureException {628Map<AlgorithmId, String> algorithms = new HashMap<>();629for (SignerInfo info : infos) {630algorithms.putAll(info.algorithms);631}632633Set<String> enabledAlgorithms = new HashSet<>();634try {635for (Map.Entry<AlgorithmId, String> algorithm : algorithms.entrySet()) {636params.setExtendedExceptionMsg(name, algorithm.getValue());637AlgorithmId algId = algorithm.getKey();638JAR_DISABLED_CHECK.permits(algId.getName(),639algId.getParameters(), params);640enabledAlgorithms.add(algId.getName());641}642} catch (CertPathValidatorException e) {643throw new SignatureException(e);644}645return enabledAlgorithms;646}647}648649650