【问题标题】:Password protected zip file in javajava中受密码保护的zip文件
【发布时间】:2012-05-22 04:29:29
【问题描述】:

我已经使用 java 创建了如下 sn-p 的 zip 文件

import java.io.*;
import java.util.zip.*;

public class ZipCreateExample {
  public static void main(String[] args) throws IOException {
    System.out.print("Please enter file name to zip : ");
    BufferedReader input = new BufferedReader
        (new InputStreamReader(System.in));
    String filesToZip = input.readLine();
    File f = new File(filesToZip);
    if(!f.exists()) {
      System.out.println("File not found.");
      System.exit(0);
    }
    System.out.print("Please enter zip file name : ");
    String zipFileName = input.readLine();
    if (!zipFileName.endsWith(".zip"))
      zipFileName = zipFileName + ".zip";
    byte[] buffer = new byte[18024];
    try {
      ZipOutputStream out = new ZipOutputStream
          (new FileOutputStream(zipFileName));
      out.setLevel(Deflater.DEFAULT_COMPRESSION);
      FileInputStream in = new FileInputStream(filesToZip);
      out.putNextEntry(new ZipEntry(filesToZip));
      int len;
      while ((len = in.read(buffer)) > 0) {
        out.write(buffer, 0, len);
      }
      out.closeEntry();
      in.close();
      out.close();
    } catch (IllegalArgumentException iae) {
      iae.printStackTrace();
      System.exit(0);
    } catch (FileNotFoundException fnfe) {
      fnfe.printStackTrace();
      System.exit(0);
    } catch (IOException ioe) {
      ioe.printStackTrace();
      System.exit(0);
    }
  }
}

现在我希望当我单击 zip 文件时,它应该提示我输入密码,然后解压缩 zip 文件。 请任何帮助,我应该如何走得更远?

【问题讨论】:

  • 您必须更清楚问题所在。当您尝试打开 zip 文件时实际发生了什么?

标签: java zip


【解决方案1】:

图书馆 Zip4J 似乎是首选答案。 如果强烈建议密码的私密性,则可以关闭class ZipFile 中的安全漏洞,该漏洞以纯文本 形式携带密码,即使在 ZipFile 关闭后也是如此。以下方法会破坏密码。

public static void destroyZipPassword(ZipFile zip) throws DestroyFailedException
{
    try
    {
        Field fdPwd = ZipFile.class.getDeclaredField("password");
        fdPwd.setAccessible(true);
        char[] password = (char[]) fdPwd.get(zip);
        Arrays.fill(password, (char) 0);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        throw new DestroyFailedException(e.getMessage());
    }
}

【讨论】:

    【解决方案2】:

    标准 Java API 不支持受密码保护的 zip 文件。幸运的是,好人已经为我们实现了这种能力。请查看这篇解释如何创建受密码保护的 zip 的文章。
    (链接失效,最新存档版本:https://web.archive.org/web/20161029174700/http://java.sys-con.com/node/1258827

    【讨论】:

    • 这里提到了其他选项:stackoverflow.com/questions/166340/…
    • 不确定你是否还在@AlexR附近,但你的链接不再有效:​​(
    • @SolarLunix 它可以工作,也许当你尝试它时网站已经关闭了一段时间。
    • @AlexR 链接在哪里?
    • @shihabudheenk 原来的网站已经死了,所以有人删除了链接。我添加了一个存档版本。
    【解决方案3】:

    在新版本的 Zip4j 中,删除了类 Zip4jConstants。请改用 EncryptionMethodAesKeyStrength 类。文档:https://github.com/srikanth-lingala/zip4j

    ZipParameters zipParameters = new ZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); 
    
    List<File> filesToAdd = Arrays.asList(
        new File("somefile"), 
        new File("someotherfile")
    );
    
    ZipFile zipFile = new ZipFile("filename.zip", "password".toCharArray());
    zipFile.addFiles(filesToAdd, zipParameters);
    

    【讨论】:

      【解决方案4】:

      试试下面基于Zip4j的代码:

      import net.lingala.zip4j.core.ZipFile;
      import net.lingala.zip4j.exception.ZipException;
      import net.lingala.zip4j.model.ZipParameters;
      import net.lingala.zip4j.util.Zip4jConstants;
      import org.apache.commons.io.FilenameUtils;
      
      import java.io.File;
      
      public class Zipper
      {
          private String password;
          private static final String EXTENSION = "zip";
      
          public Zipper(String password)
          {
              this.password = password;
          }
      
          public void pack(String filePath) throws ZipException
          {
              ZipParameters zipParameters = new ZipParameters();
              zipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
              zipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_ULTRA);
              zipParameters.setEncryptFiles(true);
              zipParameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
              zipParameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
              zipParameters.setPassword(password);
              String baseFileName = FilenameUtils.getBaseName(filePath);
              String destinationZipFilePath = baseFileName + "." + EXTENSION;
              ZipFile zipFile = new ZipFile(destinationZipFilePath);
              zipFile.addFile(new File(filePath), zipParameters);
          }
      
          public void unpack(String sourceZipFilePath, String extractedZipFilePath) throws ZipException
          {
              ZipFile zipFile = new ZipFile(sourceZipFilePath + "." + EXTENSION);
      
              if (zipFile.isEncrypted())
              {
                  zipFile.setPassword(password);
              }
      
              zipFile.extractAll(extractedZipFilePath);
          }
      }
      

      FilenameUtils 来自Apache Commons IO

      示例用法:

      public static void main(String[] arguments) throws ZipException
      {
          Zipper zipper = new Zipper("password");
          zipper.pack("encrypt-me.txt");
          zipper.unpack("encrypt-me", "D:\\");
      }
      

      【讨论】:

      【解决方案5】:

      下面的示例代码将压缩和密码保护您的文件。 此 REST 服务接受原始文件的字节。它压缩字节数组并用密码保护它。然后它发送密码保护的压缩文件字节作为响应。该代码是向 REST 服务发送和接收二进制字节以及使用密码保护压缩文件的示例。字节是从流中压缩的,因此服务器上不会存储任何文件。

      • 使用 JAX-RS API 在 java 中使用 Jersey API
      • 客户端正在使用 Jersey-client API。
      • 使用 zip4j 1.3.2 开源库和 apache commons io。
      
      
          @PUT
          @Path("/bindata/protect/qparam")
          @Consumes(MediaType.APPLICATION_OCTET_STREAM)
          @Produces(MediaType.APPLICATION_OCTET_STREAM)
          public Response zipFileUsingPassProtect(byte[] fileBytes, @QueryParam(value = "pass") String pass,
                  @QueryParam(value = "inputFileName") String inputFileName) {
      
              System.out.println("====2001==== Entering zipFileUsingPassProtect");
              System.out.println("fileBytes size = " + fileBytes.length);
              System.out.println("password = " + pass);
              System.out.println("inputFileName = " + inputFileName);
      
              byte b[] = null;
              try {
                  b = zipFileProtected(fileBytes, inputFileName, pass);
              } catch (IOException e) {
                  e.printStackTrace();
                  return Response.status(Status.INTERNAL_SERVER_ERROR).build();
              }
              System.out.println(" ");
              System.out.println("++++++++++++++++++++++++++++++++");
              System.out.println(" ");
              return Response.ok(b, MediaType.APPLICATION_OCTET_STREAM)
                      .header("content-disposition", "attachment; filename = " + inputFileName + ".zip").build();
      
          }
      
          private byte[] zipFileProtected(byte[] fileBytes, String fileName, String pass) throws IOException {
      
              ByteArrayInputStream inputByteStream = null;
              ByteArrayOutputStream outputByteStream = null;
              net.lingala.zip4j.io.ZipOutputStream outputZipStream = null;
      
              try {
                  //write the zip bytes to a byte array
                  outputByteStream = new ByteArrayOutputStream();
                  outputZipStream = new net.lingala.zip4j.io.ZipOutputStream(outputByteStream);
      
                  //input byte stream to read the input bytes
                  inputByteStream = new ByteArrayInputStream(fileBytes);
      
                  //init the zip parameters
                  ZipParameters zipParams = new ZipParameters();
                  zipParams.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
                  zipParams.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
                  zipParams.setEncryptFiles(true);
                  zipParams.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
                  zipParams.setPassword(pass);
                  zipParams.setSourceExternalStream(true);
                  zipParams.setFileNameInZip(fileName);
      
                  //create zip entry
                  outputZipStream.putNextEntry(new File(fileName), zipParams);
                  IOUtils.copy(inputByteStream, outputZipStream);
                  outputZipStream.closeEntry();
      
                  //finish up
                  outputZipStream.finish();
      
                  IOUtils.closeQuietly(inputByteStream);
                  IOUtils.closeQuietly(outputByteStream);
                  IOUtils.closeQuietly(outputZipStream);
      
                  return outputByteStream.toByteArray();
      
              } catch (ZipException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              } finally {
                  IOUtils.closeQuietly(inputByteStream);
                  IOUtils.closeQuietly(outputByteStream);
                  IOUtils.closeQuietly(outputZipStream);
              }
              return null;
          }
      
      

      下面的单元测试:

      
          @Test
          public void testPassProtectZip_with_params() {
              byte[] inputBytes = null;
              try {
                  inputBytes = FileUtils.readFileToByteArray(new File(inputFilePath));
              } catch (IOException e) {
                  e.printStackTrace();
              }
              System.out.println("bytes read into array. size = " + inputBytes.length);
      
              Client client = ClientBuilder.newClient();
      
              WebTarget target = client.target("http://localhost:8080").path("filezip/services/zip/bindata/protect/qparam");
              target = target.queryParam("pass", "mypass123");
              target = target.queryParam("inputFileName", "any_name_here.pdf");
      
              Invocation.Builder builder = target.request(MediaType.APPLICATION_OCTET_STREAM);
      
              Response resp = builder.put(Entity.entity(inputBytes, MediaType.APPLICATION_OCTET_STREAM));
              System.out.println("response = " + resp.getStatus());
              Assert.assertEquals(Status.OK.getStatusCode(), resp.getStatus());
      
              byte[] zipBytes = resp.readEntity(byte[].class);
              try {
                  FileUtils.writeByteArrayToFile(new File(responseFilePathPasswordZipParam), zipBytes);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      随意使用和修改。如果您发现任何错误,请告诉我。希望这可以帮助。

      编辑 1 - 使用 QueryParam 但您可以使用 HeaderParam 代替 PUT 以隐藏 passwd。相应地修改测试方法。

      编辑 2 - REST 路径是 filezip/services/zip/bindata/protect/qparam

      filezip 是战争的名称。 services 是 web.xml 中的 url 映射。 zip 是类级别的路径注释。 bindata/protect/qparam 是方法级别的路径注释。

      【讨论】:

      • 为什么我们需要创建一个ByteArrayInputStream inputByteStream。我们可以简化它并只使用它: outputZipStream.write(fileBytes) @RuntimeException
      【解决方案6】:

      没有用于创建密码保护文件的默认 Java API。还有另一个例子说明如何做到这一点here

      【讨论】:

      • 提供的链接已损坏
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 2020-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多