2525import java .net .MalformedURLException ;
2626import java .net .URL ;
2727import java .util .ArrayList ;
28+ import java .util .HashMap ;
2829import java .util .List ;
30+ import java .util .Map ;
2931import java .util .zip .ZipEntry ;
3032import java .util .zip .ZipInputStream ;
3133import java .util .zip .ZipOutputStream ;
@@ -199,9 +201,11 @@ public void execute() throws MojoExecutionException {
199201 oldSslDebug = System .setProperty ("javax.net.debug" ,"all" );
200202 }
201203
204+ SignedFiles signedFiles = new SignedFiles (filesToSign );
205+
202206 try {
203- String signingSetID = makeSigningRequest (filesToSign );
204- downloadSignedFiles (filesToSign , signingSetID );
207+ String signingSetID = makeSigningRequest (signedFiles );
208+ downloadSignedFiles (signedFiles , signingSetID );
205209 } catch (SOAPException | IOException e ) {
206210 throw new MojoExecutionException ("Signing failed : " + e .getMessage (), e );
207211 } finally {
@@ -216,7 +220,7 @@ public void execute() throws MojoExecutionException {
216220 }
217221
218222
219- private String makeSigningRequest (List < File > filesToSign ) throws SOAPException , IOException , MojoExecutionException {
223+ private String makeSigningRequest (SignedFiles signedFiles ) throws SOAPException , IOException , MojoExecutionException {
220224 log ("Constructing the code signing request" );
221225
222226 SOAPMessage message = SOAP_MSG_FACTORY .createMessage ();
@@ -240,15 +244,13 @@ private String makeSigningRequest(List<File> filesToSign) throws SOAPException,
240244 requestSigningRequest .addChildElement ("signingServiceName" , NS );
241245 signingServiceName .addTextNode (this .signingService );
242246
243- List <String > fileNames = getFileNames (filesToSign );
244-
245247 SOAPElement commaDelimitedFileNames =
246248 requestSigningRequest .addChildElement ("commaDelimitedFileNames" , NS );
247- commaDelimitedFileNames .addTextNode (StringUtils . join ( fileNames . iterator (), "," ));
249+ commaDelimitedFileNames .addTextNode (signedFiles . getCommaSeparatedUploadFileNames ( ));
248250
249251 SOAPElement application =
250252 requestSigningRequest .addChildElement ("application" , NS );
251- application .addTextNode (getApplicationString (fileNames , filesToSign ));
253+ application .addTextNode (signedFiles . getApplicationString ());
252254
253255 // Send the message
254256 SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory .newInstance ();
@@ -332,7 +334,7 @@ public BuildException(String message) {
332334
333335 }
334336
335- private void downloadSignedFiles (List < File > filesToSign , String id )
337+ private void downloadSignedFiles (SignedFiles signedFiles , String id )
336338 throws SOAPException , IOException , BuildException {
337339
338340 log ("Downloading signed files. The signing set ID is: " + id );
@@ -387,7 +389,7 @@ private void downloadSignedFiles(List<File> filesToSign, String id)
387389 throw new BuildException ("Download failed. Result code was: " + result );
388390 }
389391
390- extractFilesFromApplicationString (data , filesToSign );
392+ signedFiles . extractFilesFromApplicationString (data );
391393 }
392394
393395
@@ -415,80 +417,92 @@ private static void addCredentials(SOAPElement requestSigningRequest,
415417 }
416418
417419
418- /**
419- * Signing service requires unique files names. Since files will be returned
420- * in order, use dummy names that we know are unique but retain the file
421- * extension since the signing service appears to use it to figure out what
422- * to sign and how to sign it.
423- */
424- private static List <String > getFileNames (List <File > filesToSign ) {
425- List <String > result = new ArrayList <>(filesToSign .size ());
426-
427- for (int i = 0 ; i < filesToSign .size (); i ++) {
428- File f = filesToSign .get (i );
429- String fileName = f .getName ();
430- int extIndex = fileName .lastIndexOf ('.' );
431- String newName ;
432- if (extIndex < 0 ) {
433- newName = Integer .toString (i );
434- } else {
435- newName = Integer .toString (i ) + fileName .substring (extIndex );
436- }
437- result .add (newName );
438- }
439- return result ;
440- }
441-
442420
443421 /**
444- * Zips the files, base 64 encodes the resulting zip and then returns the
445- * string. It would be far more efficient to stream this directly to the
446- * signing server but the files that need to be signed are relatively small
447- * and this simpler to write.
422+ * Ensures that unique file names are sent to the signing service
423+ *
424+ * <p>We can't rely on the order in which we set the file names in the signing request, since
425+ * that is not preserved in the zipped response.</p>
426+ *
427+ * <p>The file extension is kept since the signing service appears to use it to figure out what
428+ * to sign and how to sign it.</p>
448429 *
449- * @param fileNames Modified names of files
450- * @param files Files to be signed
451430 */
452- private static String getApplicationString (List <String > fileNames , List <File > files )
453- throws IOException {
454- // 16 MB should be more than enough for Tomcat
455- // TODO: Refactoring this entire class so it uses streaming rather than
456- // buffering the entire set of files in memory would make it more
457- // widely useful.
458- ByteArrayOutputStream baos = new ByteArrayOutputStream (16 * 1024 * 1024 );
459- try (ZipOutputStream zos = new ZipOutputStream (baos )) {
460- byte [] buf = new byte [32 * 1024 ];
461- for (int i = 0 ; i < files .size (); i ++) {
462- try (FileInputStream fis = new FileInputStream (files .get (i ))) {
463- ZipEntry zipEntry = new ZipEntry (fileNames .get (i ));
464- zos .putNextEntry (zipEntry );
465- int numRead ;
466- while ( (numRead = fis .read (buf )) >= 0 ) {
467- zos .write (buf , 0 , numRead );
468- }
431+ static class SignedFiles {
432+
433+ private Map <String , File > fileNameMapping = new HashMap <>();
434+
435+ public SignedFiles (List <File > filesToSign ) {
436+ for (int i = 0 ; i < filesToSign .size (); i ++) {
437+ File f = filesToSign .get (i );
438+ String fileName = f .getName ();
439+ int extIndex = fileName .lastIndexOf ('.' );
440+ String newName ;
441+ if (extIndex < 0 ) {
442+ newName = Integer .toString (i );
443+ } else {
444+ newName = Integer .toString (i ) + fileName .substring (extIndex );
469445 }
446+
447+ fileNameMapping .put (newName , f );
470448 }
471449 }
450+
451+ public String getCommaSeparatedUploadFileNames () {
452+
453+ return StringUtils .join (fileNameMapping .keySet ().iterator (), "," );
454+ }
472455
473- return new String (Base64 .encodeBase64 (baos .toByteArray ()), "UTF-8" );
474- }
456+ /**
457+ * Zips the files, base 64 encodes the resulting zip and then returns the
458+ * string. It would be far more efficient to stream this directly to the
459+ * signing server but the files that need to be signed are relatively small
460+ * and this simpler to write.
461+ *
462+ * @throws IOException in case of any IO problems
463+ *
464+ */
465+ public String getApplicationString () throws IOException {
466+
467+ // 16 MB should be more than enough for Tomcat
468+ // TODO: Refactoring this entire class so it uses streaming rather than
469+ // buffering the entire set of files in memory would make it more
470+ // widely useful.
471+ ByteArrayOutputStream baos = new ByteArrayOutputStream (16 * 1024 * 1024 );
472+ try (ZipOutputStream zos = new ZipOutputStream (baos )) {
473+ byte [] buf = new byte [32 * 1024 ];
474+ for ( Map .Entry <String , File > entry : fileNameMapping .entrySet () ) {
475+ try (FileInputStream fis = new FileInputStream (entry .getValue ())) {
476+ ZipEntry zipEntry = new ZipEntry (entry .getKey ());
477+ zos .putNextEntry (zipEntry );
478+ int numRead ;
479+ while ( (numRead = fis .read (buf )) >= 0 ) {
480+ zos .write (buf , 0 , numRead );
481+ }
482+ }
483+ }
484+ }
475485
486+ return new String (Base64 .encodeBase64 (baos .toByteArray ()), "UTF-8" );
487+ }
476488
477- /**
478- * Removes base64 encoding, unzips the files and writes the new files over
479- * the top of the old ones.
480- */
481- private static void extractFilesFromApplicationString (String data , List <File > files )
482- throws IOException {
483- ByteArrayInputStream bais = new ByteArrayInputStream (Base64 .decodeBase64 (data .getBytes ("UTF-8" )));
484- try (ZipInputStream zis = new ZipInputStream (bais )) {
485- byte [] buf = new byte [32 * 1024 ];
486- for (int i = 0 ; i < files .size (); i ++) {
487- try (FileOutputStream fos = new FileOutputStream (files .get (i ))) {
488- zis .getNextEntry ();
489- int numRead ;
490- while ( (numRead = zis .read (buf )) >= 0 ) {
491- fos .write (buf , 0 , numRead );
489+ /**
490+ * Removes base64 encoding, unzips the files and writes the new files over
491+ * the top of the old ones.
492+ */
493+ public void extractFilesFromApplicationString (String data ) throws IOException {
494+
495+ ByteArrayInputStream bais = new ByteArrayInputStream (Base64 .decodeBase64 (data .getBytes ("UTF-8" )));
496+ try (ZipInputStream zis = new ZipInputStream (bais )) {
497+ byte [] buf = new byte [32 * 1024 ];
498+ ZipEntry entry ;
499+ while ((entry = zis .getNextEntry ()) != null ) {
500+ File outFile = fileNameMapping .get (entry .getName ());
501+ try (FileOutputStream fos = new FileOutputStream (outFile )) {
502+ int numRead ;
503+ while ((numRead = zis .read (buf )) >= 0 ) {
504+ fos .write (buf , 0 , numRead );
505+ }
492506 }
493507 }
494508 }
0 commit comments