Path: blob/master/src/java.base/share/classes/sun/security/pkcs/PKCS7.java
41159 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.*;28import java.math.BigInteger;29import java.net.URI;30import java.util.*;31import java.security.cert.X509Certificate;32import java.security.cert.CertificateException;33import java.security.cert.X509CRL;34import java.security.cert.CRLException;35import java.security.cert.CertificateFactory;36import java.security.*;37import java.util.function.Function;3839import sun.security.provider.SHAKE256;40import sun.security.timestamp.*;41import sun.security.util.*;42import sun.security.x509.*;4344/**45* PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile46* Supports only {@code SignedData} ContentInfo47* type, where to the type of data signed is plain Data.48* For signedData, {@code crls}, {@code attributes} and49* PKCS#6 Extended Certificates are not supported.50*51* @author Benjamin Renaud52*/53public class PKCS7 {5455private ObjectIdentifier contentType;5657// the ASN.1 members for a signedData (and other) contentTypes58private BigInteger version = null;59private AlgorithmId[] digestAlgorithmIds = null;60private ContentInfo contentInfo = null;61private X509Certificate[] certificates = null;62private X509CRL[] crls = null;63private SignerInfo[] signerInfos = null;6465private boolean oldStyle = false; // Is this JDK1.1.x-style?6667private Principal[] certIssuerNames;6869/*70* Random number generator for creating nonce values71* (Lazy initialization)72*/73private static class SecureRandomHolder {74static final SecureRandom RANDOM;75static {76SecureRandom tmp = null;77try {78tmp = SecureRandom.getInstance("SHA1PRNG");79} catch (NoSuchAlgorithmException e) {80// should not happen81}82RANDOM = tmp;83}84}8586/**87* Unmarshals a PKCS7 block from its encoded form, parsing the88* encoded bytes from the InputStream.89*90* @param in an input stream holding at least one PKCS7 block.91* @exception ParsingException on parsing errors.92* @exception IOException on other errors.93*/94public PKCS7(InputStream in) throws ParsingException, IOException {95DataInputStream dis = new DataInputStream(in);96byte[] data = new byte[dis.available()];97dis.readFully(data);9899parse(new DerInputStream(data));100}101102/**103* Unmarshals a PKCS7 block from its encoded form, parsing the104* encoded bytes from the DerInputStream.105*106* @param derin a DerInputStream holding at least one PKCS7 block.107* @exception ParsingException on parsing errors.108*/109public PKCS7(DerInputStream derin) throws ParsingException {110parse(derin);111}112113/**114* Unmarshals a PKCS7 block from its encoded form, parsing the115* encoded bytes.116*117* @param bytes the encoded bytes.118* @exception ParsingException on parsing errors.119*/120public PKCS7(byte[] bytes) throws ParsingException {121try {122DerInputStream derin = new DerInputStream(bytes);123parse(derin);124} catch (IOException ioe1) {125ParsingException pe = new ParsingException(126"Unable to parse the encoded bytes");127pe.initCause(ioe1);128throw pe;129}130}131132/*133* Parses a PKCS#7 block.134*/135private void parse(DerInputStream derin)136throws ParsingException137{138try {139derin.mark(derin.available());140// try new (i.e., JDK1.2) style141parse(derin, false);142} catch (IOException ioe) {143try {144derin.reset();145// try old (i.e., JDK1.1.x) style146parse(derin, true);147oldStyle = true;148} catch (IOException ioe1) {149ParsingException pe = new ParsingException(150ioe1.getMessage());151pe.initCause(ioe);152pe.addSuppressed(ioe1);153throw pe;154}155}156}157158/**159* Parses a PKCS#7 block.160*161* @param derin the ASN.1 encoding of the PKCS#7 block.162* @param oldStyle flag indicating whether or not the given PKCS#7 block163* is encoded according to JDK1.1.x.164*/165private void parse(DerInputStream derin, boolean oldStyle)166throws IOException167{168ContentInfo block = new ContentInfo(derin, oldStyle);169contentType = block.contentType;170DerValue content = block.getContent();171172if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) {173parseSignedData(content);174} else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) {175// This is for backwards compatibility with JDK 1.1.x176parseOldSignedData(content);177} else if (contentType.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){178parseNetscapeCertChain(content);179contentInfo = block; // Maybe useless, just do not let it be null180} else {181throw new ParsingException("content type " + contentType +182" not supported.");183}184}185186/**187* Construct an initialized PKCS7 block.188*189* @param digestAlgorithmIds the message digest algorithm identifiers.190* @param contentInfo the content information.191* @param certificates an array of X.509 certificates.192* @param crls an array of CRLs193* @param signerInfos an array of signer information.194*/195public PKCS7(AlgorithmId[] digestAlgorithmIds,196ContentInfo contentInfo,197X509Certificate[] certificates,198X509CRL[] crls,199SignerInfo[] signerInfos) {200201version = BigInteger.ONE;202this.digestAlgorithmIds = digestAlgorithmIds;203this.contentInfo = contentInfo;204this.certificates = certificates;205this.crls = crls;206this.signerInfos = signerInfos;207}208209public PKCS7(AlgorithmId[] digestAlgorithmIds,210ContentInfo contentInfo,211X509Certificate[] certificates,212SignerInfo[] signerInfos) {213this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos);214}215216private void parseNetscapeCertChain(DerValue val)217throws ParsingException, IOException {218DerInputStream dis = new DerInputStream(val.toByteArray());219DerValue[] contents = dis.getSequence(2);220certificates = new X509Certificate[contents.length];221222CertificateFactory certfac = null;223try {224certfac = CertificateFactory.getInstance("X.509");225} catch (CertificateException ce) {226// do nothing227}228229for (int i=0; i < contents.length; i++) {230ByteArrayInputStream bais = null;231try {232if (certfac == null)233certificates[i] = new X509CertImpl(contents[i]);234else {235byte[] encoded = contents[i].toByteArray();236bais = new ByteArrayInputStream(encoded);237certificates[i] =238(X509Certificate)certfac.generateCertificate(bais);239bais.close();240bais = null;241}242} catch (CertificateException ce) {243ParsingException pe = new ParsingException(ce.getMessage());244pe.initCause(ce);245throw pe;246} catch (IOException ioe) {247ParsingException pe = new ParsingException(ioe.getMessage());248pe.initCause(ioe);249throw pe;250} finally {251if (bais != null)252bais.close();253}254}255}256257private void parseSignedData(DerValue val)258throws ParsingException, IOException {259260DerInputStream dis = val.toDerInputStream();261262// Version263version = dis.getBigInteger();264265// digestAlgorithmIds266DerValue[] digestAlgorithmIdVals = dis.getSet(1);267int len = digestAlgorithmIdVals.length;268digestAlgorithmIds = new AlgorithmId[len];269try {270for (int i = 0; i < len; i++) {271DerValue oid = digestAlgorithmIdVals[i];272digestAlgorithmIds[i] = AlgorithmId.parse(oid);273}274275} catch (IOException e) {276ParsingException pe =277new ParsingException("Error parsing digest AlgorithmId IDs: " +278e.getMessage());279pe.initCause(e);280throw pe;281}282// contentInfo283contentInfo = new ContentInfo(dis);284285CertificateFactory certfac = null;286try {287certfac = CertificateFactory.getInstance("X.509");288} catch (CertificateException ce) {289// do nothing290}291292/*293* check if certificates (implicit tag) are provided294* (certificates are OPTIONAL)295*/296if ((byte)(dis.peekByte()) == (byte)0xA0) {297DerValue[] certVals = dis.getSet(2, true);298299len = certVals.length;300certificates = new X509Certificate[len];301int count = 0;302303for (int i = 0; i < len; i++) {304ByteArrayInputStream bais = null;305try {306byte tag = certVals[i].getTag();307// We only parse the normal certificate. Other types of308// CertificateChoices ignored.309if (tag == DerValue.tag_Sequence) {310if (certfac == null) {311certificates[count] = new X509CertImpl(certVals[i]);312} else {313byte[] encoded = certVals[i].toByteArray();314bais = new ByteArrayInputStream(encoded);315certificates[count] =316(X509Certificate)certfac.generateCertificate(bais);317bais.close();318bais = null;319}320count++;321}322} catch (CertificateException ce) {323ParsingException pe = new ParsingException(ce.getMessage());324pe.initCause(ce);325throw pe;326} catch (IOException ioe) {327ParsingException pe = new ParsingException(ioe.getMessage());328pe.initCause(ioe);329throw pe;330} finally {331if (bais != null)332bais.close();333}334}335if (count != len) {336certificates = Arrays.copyOf(certificates, count);337}338}339340// check if crls (implicit tag) are provided (crls are OPTIONAL)341if ((byte)(dis.peekByte()) == (byte)0xA1) {342DerValue[] crlVals = dis.getSet(1, true);343344len = crlVals.length;345crls = new X509CRL[len];346347for (int i = 0; i < len; i++) {348ByteArrayInputStream bais = null;349try {350if (certfac == null)351crls[i] = new X509CRLImpl(crlVals[i]);352else {353byte[] encoded = crlVals[i].toByteArray();354bais = new ByteArrayInputStream(encoded);355crls[i] = (X509CRL) certfac.generateCRL(bais);356bais.close();357bais = null;358}359} catch (CRLException e) {360ParsingException pe =361new ParsingException(e.getMessage());362pe.initCause(e);363throw pe;364} finally {365if (bais != null)366bais.close();367}368}369}370371// signerInfos372DerValue[] signerInfoVals = dis.getSet(1);373374len = signerInfoVals.length;375signerInfos = new SignerInfo[len];376377for (int i = 0; i < len; i++) {378DerInputStream in = signerInfoVals[i].toDerInputStream();379signerInfos[i] = new SignerInfo(in);380}381}382383/*384* Parses an old-style SignedData encoding (for backwards385* compatibility with JDK1.1.x).386*/387private void parseOldSignedData(DerValue val)388throws ParsingException, IOException389{390DerInputStream dis = val.toDerInputStream();391392// Version393version = dis.getBigInteger();394395// digestAlgorithmIds396DerValue[] digestAlgorithmIdVals = dis.getSet(1);397int len = digestAlgorithmIdVals.length;398399digestAlgorithmIds = new AlgorithmId[len];400try {401for (int i = 0; i < len; i++) {402DerValue oid = digestAlgorithmIdVals[i];403digestAlgorithmIds[i] = AlgorithmId.parse(oid);404}405} catch (IOException e) {406throw new ParsingException("Error parsing digest AlgorithmId IDs");407}408409// contentInfo410contentInfo = new ContentInfo(dis, true);411412// certificates413CertificateFactory certfac = null;414try {415certfac = CertificateFactory.getInstance("X.509");416} catch (CertificateException ce) {417// do nothing418}419DerValue[] certVals = dis.getSet(2);420len = certVals.length;421certificates = new X509Certificate[len];422423for (int i = 0; i < len; i++) {424ByteArrayInputStream bais = null;425try {426if (certfac == null)427certificates[i] = new X509CertImpl(certVals[i]);428else {429byte[] encoded = certVals[i].toByteArray();430bais = new ByteArrayInputStream(encoded);431certificates[i] =432(X509Certificate)certfac.generateCertificate(bais);433bais.close();434bais = null;435}436} catch (CertificateException ce) {437ParsingException pe = new ParsingException(ce.getMessage());438pe.initCause(ce);439throw pe;440} catch (IOException ioe) {441ParsingException pe = new ParsingException(ioe.getMessage());442pe.initCause(ioe);443throw pe;444} finally {445if (bais != null)446bais.close();447}448}449450// crls are ignored.451dis.getSet(0);452453// signerInfos454DerValue[] signerInfoVals = dis.getSet(1);455len = signerInfoVals.length;456signerInfos = new SignerInfo[len];457for (int i = 0; i < len; i++) {458DerInputStream in = signerInfoVals[i].toDerInputStream();459signerInfos[i] = new SignerInfo(in, true);460}461}462463/**464* Encodes the signed data to an output stream.465*466* @param out the output stream to write the encoded data to.467* @exception IOException on encoding errors.468*/469public void encodeSignedData(OutputStream out) throws IOException {470DerOutputStream derout = new DerOutputStream();471encodeSignedData(derout);472out.write(derout.toByteArray());473}474475/**476* Encodes the signed data to a DerOutputStream.477*478* @param out the DerOutputStream to write the encoded data to.479* @exception IOException on encoding errors.480*/481public void encodeSignedData(DerOutputStream out)482throws IOException483{484DerOutputStream signedData = new DerOutputStream();485486// version487signedData.putInteger(version);488489// digestAlgorithmIds490signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);491492// contentInfo493contentInfo.encode(signedData);494495// certificates (optional)496if (certificates != null && certificates.length != 0) {497// cast to X509CertImpl[] since X509CertImpl implements DerEncoder498X509CertImpl[] implCerts = new X509CertImpl[certificates.length];499for (int i = 0; i < certificates.length; i++) {500if (certificates[i] instanceof X509CertImpl)501implCerts[i] = (X509CertImpl) certificates[i];502else {503try {504byte[] encoded = certificates[i].getEncoded();505implCerts[i] = new X509CertImpl(encoded);506} catch (CertificateException ce) {507throw new IOException(ce);508}509}510}511512// Add the certificate set (tagged with [0] IMPLICIT)513// to the signed data514signedData.putOrderedSetOf((byte)0xA0, implCerts);515}516517// CRLs (optional)518if (crls != null && crls.length != 0) {519// cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder520Set<X509CRLImpl> implCRLs = new HashSet<>(crls.length);521for (X509CRL crl: crls) {522if (crl instanceof X509CRLImpl)523implCRLs.add((X509CRLImpl) crl);524else {525try {526byte[] encoded = crl.getEncoded();527implCRLs.add(new X509CRLImpl(encoded));528} catch (CRLException ce) {529throw new IOException(ce);530}531}532}533534// Add the CRL set (tagged with [1] IMPLICIT)535// to the signed data536signedData.putOrderedSetOf((byte)0xA1,537implCRLs.toArray(new X509CRLImpl[implCRLs.size()]));538}539540// signerInfos541signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);542543// making it a signed data block544DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,545signedData.toByteArray());546547// making it a content info sequence548ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,549signedDataSeq);550551// writing out the contentInfo sequence552block.encode(out);553}554555/**556* This verifies a given SignerInfo.557*558* @param info the signer information.559* @param bytes the DER encoded content information.560*561* @exception NoSuchAlgorithmException on unrecognized algorithms.562* @exception SignatureException on signature handling errors.563*/564public SignerInfo verify(SignerInfo info, byte[] bytes)565throws NoSuchAlgorithmException, SignatureException {566return info.verify(this, bytes);567}568569/**570* Returns all signerInfos which self-verify.571*572* @param bytes the DER encoded content information.573*574* @exception NoSuchAlgorithmException on unrecognized algorithms.575* @exception SignatureException on signature handling errors.576*/577public SignerInfo[] verify(byte[] bytes)578throws NoSuchAlgorithmException, SignatureException {579580Vector<SignerInfo> intResult = new Vector<>();581for (int i = 0; i < signerInfos.length; i++) {582583SignerInfo signerInfo = verify(signerInfos[i], bytes);584if (signerInfo != null) {585intResult.addElement(signerInfo);586}587}588if (!intResult.isEmpty()) {589590SignerInfo[] result = new SignerInfo[intResult.size()];591intResult.copyInto(result);592return result;593}594return null;595}596597/**598* Returns all signerInfos which self-verify.599*600* @exception NoSuchAlgorithmException on unrecognized algorithms.601* @exception SignatureException on signature handling errors.602*/603public SignerInfo[] verify()604throws NoSuchAlgorithmException, SignatureException {605return verify(null);606}607608/**609* Returns the version number of this PKCS7 block.610* @return the version or null if version is not specified611* for the content type.612*/613public BigInteger getVersion() {614return version;615}616617/**618* Returns the message digest algorithms specified in this PKCS7 block.619* @return the array of Digest Algorithms or null if none are specified620* for the content type.621*/622public AlgorithmId[] getDigestAlgorithmIds() {623return digestAlgorithmIds;624}625626/**627* Returns the content information specified in this PKCS7 block.628*/629public ContentInfo getContentInfo() {630return contentInfo;631}632633/**634* Returns the X.509 certificates listed in this PKCS7 block.635* @return a clone of the array of X.509 certificates or null if636* none are specified for the content type.637*/638public X509Certificate[] getCertificates() {639if (certificates != null)640return certificates.clone();641else642return null;643}644645/**646* Returns the X.509 crls listed in this PKCS7 block.647* @return a clone of the array of X.509 crls or null if none648* are specified for the content type.649*/650public X509CRL[] getCRLs() {651if (crls != null)652return crls.clone();653else654return null;655}656657/**658* Returns the signer's information specified in this PKCS7 block.659* @return the array of Signer Infos or null if none are specified660* for the content type.661*/662public SignerInfo[] getSignerInfos() {663return signerInfos;664}665666/**667* Returns the X.509 certificate listed in this PKCS7 block668* which has a matching serial number and Issuer name, or669* null if one is not found.670*671* @param serial the serial number of the certificate to retrieve.672* @param issuerName the Distinguished Name of the Issuer.673*/674public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) {675if (certificates != null) {676if (certIssuerNames == null)677populateCertIssuerNames();678for (int i = 0; i < certificates.length; i++) {679X509Certificate cert = certificates[i];680BigInteger thisSerial = cert.getSerialNumber();681if (serial.equals(thisSerial)682&& issuerName.equals(certIssuerNames[i]))683{684return cert;685}686}687}688return null;689}690691/**692* Populate array of Issuer DNs from certificates and convert693* each Principal to type X500Name if necessary.694*/695@SuppressWarnings("deprecation")696private void populateCertIssuerNames() {697if (certificates == null)698return;699700certIssuerNames = new Principal[certificates.length];701for (int i = 0; i < certificates.length; i++) {702X509Certificate cert = certificates[i];703Principal certIssuerName = cert.getIssuerDN();704if (!(certIssuerName instanceof X500Name)) {705// must extract the original encoded form of DN for706// subsequent name comparison checks (converting to a707// String and back to an encoded DN could cause the708// types of String attribute values to be changed)709try {710X509CertInfo tbsCert =711new X509CertInfo(cert.getTBSCertificate());712certIssuerName = (Principal)713tbsCert.get(X509CertInfo.ISSUER + "." +714X509CertInfo.DN_NAME);715} catch (Exception e) {716// error generating X500Name object from the cert's717// issuer DN, leave name as is.718}719}720certIssuerNames[i] = certIssuerName;721}722}723724/**725* Returns the PKCS7 block in a printable string form.726*/727public String toString() {728String out = "";729730out += contentInfo + "\n";731if (version != null)732out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n";733if (digestAlgorithmIds != null) {734out += "PKCS7 :: digest AlgorithmIds: \n";735for (int i = 0; i < digestAlgorithmIds.length; i++)736out += "\t" + digestAlgorithmIds[i] + "\n";737}738if (certificates != null) {739out += "PKCS7 :: certificates: \n";740for (int i = 0; i < certificates.length; i++)741out += "\t" + i + ". " + certificates[i] + "\n";742}743if (crls != null) {744out += "PKCS7 :: crls: \n";745for (int i = 0; i < crls.length; i++)746out += "\t" + i + ". " + crls[i] + "\n";747}748if (signerInfos != null) {749out += "PKCS7 :: signer infos: \n";750for (int i = 0; i < signerInfos.length; i++)751out += ("\t" + i + ". " + signerInfos[i] + "\n");752}753return out;754}755756/**757* Returns true if this is a JDK1.1.x-style PKCS#7 block, and false758* otherwise.759*/760public boolean isOldStyle() {761return this.oldStyle;762}763764/**765* Generate a PKCS7 data block.766*767* @param sigalg signature algorithm to be used768* @param sigProvider (optional) provider769* @param privateKey signer's private ky770* @param signerChain signer's certificate chain771* @param content the content to sign772* @param internalsf whether the content should be include in output773* @param directsign if the content is signed directly or thru authattrs774* @param ts (optional) timestamper775* @return the pkcs7 output in an array776* @throws SignatureException if signing failed777* @throws InvalidKeyException if key cannot be used778* @throws IOException should not happen here, all byte array779* @throws NoSuchAlgorithmException if siglag is bad780*/781public static byte[] generateNewSignedData(782String sigalg, Provider sigProvider,783PrivateKey privateKey, X509Certificate[] signerChain,784byte[] content, boolean internalsf, boolean directsign,785Function<byte[], PKCS9Attributes> ts)786throws SignatureException, InvalidKeyException, IOException,787NoSuchAlgorithmException {788789Signature signer = SignatureUtil.fromKey(sigalg, privateKey, sigProvider);790791AlgorithmId digAlgID = SignatureUtil.getDigestAlgInPkcs7SignerInfo(792signer, sigalg, privateKey, directsign);793AlgorithmId sigAlgID = SignatureUtil.fromSignature(signer, privateKey);794795PKCS9Attributes authAttrs = null;796if (!directsign) {797// MessageDigest798byte[] md;799String digAlgName = digAlgID.getName();800if (digAlgName.equals("SHAKE256") || digAlgName.equals("SHAKE256-LEN")) {801// No MessageDigest impl for SHAKE256 yet802var shaker = new SHAKE256(64);803shaker.update(content, 0, content.length);804md = shaker.digest();805} else {806md = MessageDigest.getInstance(digAlgName)807.digest(content);808}809// CMSAlgorithmProtection (RFC6211)810DerOutputStream derAp = new DerOutputStream();811DerOutputStream derAlgs = new DerOutputStream();812digAlgID.derEncode(derAlgs);813DerOutputStream derSigAlg = new DerOutputStream();814sigAlgID.derEncode(derSigAlg);815derAlgs.writeImplicit((byte)0xA1, derSigAlg);816derAp.write(DerValue.tag_Sequence, derAlgs);817authAttrs = new PKCS9Attributes(new PKCS9Attribute[]{818new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID,819ContentInfo.DATA_OID),820new PKCS9Attribute(PKCS9Attribute.SIGNING_TIME_OID,821new Date()),822new PKCS9Attribute(PKCS9Attribute.CMS_ALGORITHM_PROTECTION_OID,823derAp.toByteArray()),824new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID,825md)826});827signer.update(authAttrs.getDerEncoding());828} else {829signer.update(content);830}831832byte[] signature = signer.sign();833834return constructToken(signature, signerChain,835internalsf ? content : null,836authAttrs,837ts == null ? null : ts.apply(signature),838digAlgID,839sigAlgID);840}841842/**843* Assemble a PKCS7 token from its components844* @param signature the signature845* @param signerChain the signer's certificate chain846* @param content (optional) encapsulated content847* @param authAttrs (optional) authenticated attributes848* @param unauthAttrs (optional) unauthenticated attributes849* @param digAlgID digest algorithm identifier850* @param encAlgID encryption algorithm identifier851* @return the token in a byte array852* @throws IOException should not happen here, all byte array853*/854private static byte[] constructToken(byte[] signature,855X509Certificate[] signerChain,856byte[] content,857PKCS9Attributes authAttrs,858PKCS9Attributes unauthAttrs,859AlgorithmId digAlgID,860AlgorithmId encAlgID)861throws IOException {862// Create the SignerInfo863X500Name issuerName =864X500Name.asX500Name(signerChain[0].getIssuerX500Principal());865BigInteger serialNumber = signerChain[0].getSerialNumber();866SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,867digAlgID, authAttrs,868encAlgID,869signature, unauthAttrs);870871// Create the PKCS #7 signed data message872SignerInfo[] signerInfos = {signerInfo};873AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};874// Include or exclude content875ContentInfo contentInfo = (content == null)876? new ContentInfo(ContentInfo.DATA_OID, null)877: new ContentInfo(content);878PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,879signerChain, signerInfos);880ByteArrayOutputStream p7out = new ByteArrayOutputStream();881pkcs7.encodeSignedData(p7out);882883return p7out.toByteArray();884}885886/**887* Assembles a PKCS #7 signed data message that optionally includes a888* signature timestamp.889*890* @param signature the signature bytes891* @param signerChain the signer's X.509 certificate chain892* @param content the content that is signed; specify null to not include893* it in the PKCS7 data894* @param signatureAlgorithm the name of the signature algorithm895* @param tsaURI the URI of the Timestamping Authority; or null if no896* timestamp is requested897* @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a898* numerical object identifier; or null if we leave the TSA server899* to choose one. This argument is only used when tsaURI is provided900* @return the bytes of the encoded PKCS #7 signed data message901* @throws NoSuchAlgorithmException The exception is thrown if the signature902* algorithm is unrecognised.903* @throws CertificateException The exception is thrown if an error occurs904* while processing the signer's certificate or the TSA's905* certificate.906* @throws IOException The exception is thrown if an error occurs while907* generating the signature timestamp or while generating the signed908* data message.909*/910@Deprecated(since="16", forRemoval=true)911public static byte[] generateSignedData(byte[] signature,912X509Certificate[] signerChain,913byte[] content,914String signatureAlgorithm,915URI tsaURI,916String tSAPolicyID,917String tSADigestAlg)918throws CertificateException, IOException, NoSuchAlgorithmException919{920921// Generate the timestamp token922PKCS9Attributes unauthAttrs = null;923if (tsaURI != null) {924// Timestamp the signature925HttpTimestamper tsa = new HttpTimestamper(tsaURI);926byte[] tsToken = generateTimestampToken(927tsa, tSAPolicyID, tSADigestAlg, signature);928929// Insert the timestamp token into the PKCS #7 signer info element930// (as an unsigned attribute)931unauthAttrs =932new PKCS9Attributes(new PKCS9Attribute[]{933new PKCS9Attribute(934PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID,935tsToken)});936}937938return constructToken(signature, signerChain, content,939null,940unauthAttrs,941AlgorithmId.get(SignatureUtil.extractDigestAlgFromDwithE(signatureAlgorithm)),942AlgorithmId.get(signatureAlgorithm));943}944945/**946* Examine the certificate for a Subject Information Access extension947* (<a href="http://tools.ietf.org/html/rfc5280">RFC 5280</a>).948* The extension's {@code accessMethod} field should contain the object949* identifier defined for timestamping: 1.3.6.1.5.5.7.48.3 and its950* {@code accessLocation} field should contain an HTTP or HTTPS URL.951*952* @param tsaCertificate (optional) X.509 certificate for the TSA.953* @return An HTTP or HTTPS URI or null if none was found.954*/955public static URI getTimestampingURI(X509Certificate tsaCertificate) {956957if (tsaCertificate == null) {958return null;959}960// Parse the extensions961try {962byte[] extensionValue = tsaCertificate.getExtensionValue963(KnownOIDs.SubjectInfoAccess.value());964if (extensionValue == null) {965return null;966}967DerInputStream der = new DerInputStream(extensionValue);968der = new DerInputStream(der.getOctetString());969DerValue[] derValue = der.getSequence(5);970AccessDescription description;971GeneralName location;972URIName uri;973for (int i = 0; i < derValue.length; i++) {974description = new AccessDescription(derValue[i]);975if (description.getAccessMethod()976.equals(ObjectIdentifier.of(KnownOIDs.AD_TimeStamping))) {977location = description.getAccessLocation();978if (location.getType() == GeneralNameInterface.NAME_URI) {979uri = (URIName) location.getName();980if (uri.getScheme().equalsIgnoreCase("http") ||981uri.getScheme().equalsIgnoreCase("https")) {982return uri.getURI();983}984}985}986}987} catch (IOException ioe) {988// ignore989}990return null;991}992993/**994* Requests, processes and validates a timestamp token from a TSA using995* common defaults. Uses the following defaults in the timestamp request:996* SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate997* set to true.998*999* @param tsa the timestamping authority to use1000* @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a1001* numerical object identifier; or null if we leave the TSA server1002* to choose one1003* @param toBeTimestamped the token that is to be timestamped1004* @return the encoded timestamp token1005* @throws IOException The exception is thrown if an error occurs while1006* communicating with the TSA, or a non-null1007* TSAPolicyID is specified in the request but it1008* does not match the one in the reply1009* @throws CertificateException The exception is thrown if the TSA's1010* certificate is not permitted for timestamping.1011*/1012public static byte[] generateTimestampToken(Timestamper tsa,1013String tSAPolicyID,1014String tSADigestAlg,1015byte[] toBeTimestamped)1016throws IOException, CertificateException1017{1018// Generate a timestamp1019MessageDigest messageDigest = null;1020TSRequest tsQuery = null;1021try {1022messageDigest = MessageDigest.getInstance(tSADigestAlg);1023tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);1024} catch (NoSuchAlgorithmException e) {1025throw new IllegalArgumentException(e);1026}10271028// Generate a nonce1029BigInteger nonce = null;1030if (SecureRandomHolder.RANDOM != null) {1031nonce = new BigInteger(64, SecureRandomHolder.RANDOM);1032tsQuery.setNonce(nonce);1033}1034tsQuery.requestCertificate(true);10351036TSResponse tsReply = tsa.generateTimestamp(tsQuery);1037int status = tsReply.getStatusCode();1038// Handle TSP error1039if (status != 0 && status != 1) {1040throw new IOException("Error generating timestamp: " +1041tsReply.getStatusCodeAsText() + " " +1042tsReply.getFailureCodeAsText());1043}10441045if (tSAPolicyID != null &&1046!tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) {1047throw new IOException("TSAPolicyID changed in "1048+ "timestamp token");1049}1050PKCS7 tsToken = tsReply.getToken();10511052TimestampToken tst = tsReply.getTimestampToken();1053try {1054if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) {1055throw new IOException("Digest algorithm not " + tSADigestAlg + " in "1056+ "timestamp token");1057}1058} catch (NoSuchAlgorithmException nase) {1059throw new IllegalArgumentException(); // should have been caught before1060}1061if (!MessageDigest.isEqual(tst.getHashedMessage(),1062tsQuery.getHashedMessage())) {1063throw new IOException("Digest octets changed in timestamp token");1064}10651066BigInteger replyNonce = tst.getNonce();1067if (replyNonce == null && nonce != null) {1068throw new IOException("Nonce missing in timestamp token");1069}1070if (replyNonce != null && !replyNonce.equals(nonce)) {1071throw new IOException("Nonce changed in timestamp token");1072}10731074// Examine the TSA's certificate (if present)1075for (SignerInfo si: tsToken.getSignerInfos()) {1076X509Certificate cert = si.getCertificate(tsToken);1077if (cert == null) {1078// Error, we've already set tsRequestCertificate = true1079throw new CertificateException(1080"Certificate not included in timestamp token");1081} else {1082if (!cert.getCriticalExtensionOIDs().contains(1083KnownOIDs.extendedKeyUsage.value())) {1084throw new CertificateException(1085"Certificate is not valid for timestamping");1086}1087List<String> keyPurposes = cert.getExtendedKeyUsage();1088if (keyPurposes == null ||1089!keyPurposes.contains(KnownOIDs.KP_TimeStamping.value())) {1090throw new CertificateException(1091"Certificate is not valid for timestamping");1092}1093}1094}1095return tsReply.getEncodedToken();1096}1097}109810991100