1 /* 2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 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 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.tools.jarsigner; 27 28 import java.io.IOException; 29 import java.net.URI; 30 import java.security.NoSuchAlgorithmException; 31 import java.security.cert.CertificateException; 32 import java.security.cert.X509Certificate; 33 34 import com.sun.jarsigner.*; 35 import sun.security.pkcs.PKCS7; 36 import sun.security.util.*; 37 import sun.security.x509.*; 38 39 /** 40 * This class implements a content signing service. 41 * It generates a timestamped signature for a given content according to 42 * <a href="http://www.ietf.org/rfc/rfc3161.txt">RFC 3161</a>. 43 * The signature along with a trusted timestamp and the signer's certificate 44 * are all packaged into a standard PKCS #7 Signed Data message. 45 * 46 * @author Vincent Ryan 47 */ 48 @SuppressWarnings("deprecation") 49 public final class TimestampedSigner extends ContentSigner { 50 51 /* 52 * Object identifier for the subject information access X.509 certificate 53 * extension. 54 */ 55 private static final String SUBJECT_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.11"; 56 57 /* 58 * Object identifier for the timestamping access descriptors. 59 */ 60 private static final ObjectIdentifier AD_TIMESTAMPING_Id; 61 static { 62 ObjectIdentifier tmp = null; 63 try { 64 tmp = new ObjectIdentifier("1.3.6.1.5.5.7.48.3"); 65 } catch (IOException e) { 66 // ignore 67 } 68 AD_TIMESTAMPING_Id = tmp; 69 } 70 71 /** 72 * Instantiates a content signer that supports timestamped signatures. 73 */ 74 public TimestampedSigner() { 75 } 76 77 /** 78 * Generates a PKCS #7 signed data message that includes a signature 79 * timestamp. 80 * This method is used when a signature has already been generated. 81 * The signature, a signature timestamp, the signer's certificate chain, 82 * and optionally the content that was signed, are packaged into a PKCS #7 83 * signed data message. 84 * 85 * @param params The non-null input parameters. 86 * @param omitContent true if the content should be omitted from the 87 * signed data message. Otherwise the content is included. 88 * @param applyTimestamp true if the signature should be timestamped. 89 * Otherwise timestamping is not performed. 90 * @return A PKCS #7 signed data message including a signature timestamp. 91 * @throws NoSuchAlgorithmException The exception is thrown if the signature 92 * algorithm is unrecognised. 93 * @throws CertificateException The exception is thrown if an error occurs 94 * while processing the signer's certificate or the TSA's 95 * certificate. 96 * @throws IOException The exception is thrown if an error occurs while 97 * generating the signature timestamp or while generating the signed 98 * data message. 99 * @throws NullPointerException The exception is thrown if parameters is 100 * null. 101 */ 102 public byte[] generateSignedData(ContentSignerParameters params, 103 boolean omitContent, boolean applyTimestamp) 104 throws NoSuchAlgorithmException, CertificateException, IOException { 105 106 if (params == null) { 107 throw new NullPointerException(); 108 } 109 110 // Parse the signature algorithm to extract the digest 111 // algorithm. The expected format is: 112 // "<digest>with<encryption>" 113 // or "<digest>with<encryption>and<mgf>" 114 String signatureAlgorithm = params.getSignatureAlgorithm(); 115 116 X509Certificate[] signerChain = params.getSignerCertificateChain(); 117 byte[] signature = params.getSignature(); 118 119 // Include or exclude content 120 byte[] content = (omitContent == true) ? null : params.getContent(); 121 122 URI tsaURI = null; 123 if (applyTimestamp) { 124 tsaURI = params.getTimestampingAuthority(); 125 if (tsaURI == null) { 126 // Examine TSA cert 127 tsaURI = getTimestampingURI( 128 params.getTimestampingAuthorityCertificate()); 129 if (tsaURI == null) { 130 throw new CertificateException( 131 "Subject Information Access extension not found"); 132 } 133 } 134 } 135 return PKCS7.generateSignedData(signature, signerChain, content, 136 params.getSignatureAlgorithm(), tsaURI, 137 params.getTSAPolicyID(), 138 params.getTSADigestAlg()); 139 } 140 141 /** 142 * Examine the certificate for a Subject Information Access extension 143 * (<a href="http://tools.ietf.org/html/rfc5280">RFC 5280</a>). 144 * The extension's <tt>accessMethod</tt> field should contain the object 145 * identifier defined for timestamping: 1.3.6.1.5.5.7.48.3 and its 146 * <tt>accessLocation</tt> field should contain an HTTP or HTTPS URL. 147 * 148 * @param tsaCertificate An X.509 certificate for the TSA. 149 * @return An HTTP or HTTPS URI or null if none was found. 150 */ 151 public static URI getTimestampingURI(X509Certificate tsaCertificate) { 152 153 if (tsaCertificate == null) { 154 return null; 155 } 156 // Parse the extensions 157 try { 158 byte[] extensionValue = 159 tsaCertificate.getExtensionValue(SUBJECT_INFO_ACCESS_OID); 160 if (extensionValue == null) { 161 return null; 162 } 163 DerInputStream der = new DerInputStream(extensionValue); 164 der = new DerInputStream(der.getOctetString()); 165 DerValue[] derValue = der.getSequence(5); 166 AccessDescription description; 167 GeneralName location; 168 URIName uri; 169 for (int i = 0; i < derValue.length; i++) { 170 description = new AccessDescription(derValue[i]); 171 if (description.getAccessMethod() 172 .equals(AD_TIMESTAMPING_Id)) { 173 location = description.getAccessLocation(); 174 if (location.getType() == GeneralNameInterface.NAME_URI) { 175 uri = (URIName) location.getName(); 176 if (uri.getScheme().equalsIgnoreCase("http") || 177 uri.getScheme().equalsIgnoreCase("https")) { 178 return uri.getURI(); 179 } 180 } 181 } 182 } 183 } catch (IOException ioe) { 184 // ignore 185 } 186 return null; 187 } 188 }