2014-12-12 5 views
3

PDFBOX에서 제공되는이 우수한 샘플 덕분에 PDFBOX 1.8.5를 사용하여 PDF 문서에 디지털 서명을 할 수 있습니다.PDFBox 1.8 - TSA (Time Stamp Autority)를 사용하여 PDF 문서 서명

https://github.com/apache/pdfbox/blob/1.8/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java

이 샘플은 로컬 컴퓨터의 날짜/시간 (라인 175)를 사용하여 서명 :

// 유효한 서명 signature.setSignDate에 필요한 서명 날짜 (Calendar.getInstance ());

즉, Acrobat Reader는 서명 날짜를 외부 타임 스탬프 기관 (TSA)을 사용하여 작성한 것처럼 신뢰하지 않습니다.

PDFBOX와 함께 외부 TSA를 사용하는 방법을 아는 사람이 있습니까?

감사합니다.

+0

1) 현재 버전 (1.8.8)으로 업데이트하십시오. PDF의 "구조적"부분에 몇 가지 버그 수정이있었습니다. 2) mkl의 대답이 좋은지 (나는 생각한다), 또는 문제가 있는지/더 이상의 도움이 필요한지 여부를 알려주십시오. –

답변

3

CreateSignature PDFBox 예제는 일부 TSA의 타임 스탬프를 선택적으로 포함하도록 2.0.0-SNAPSHOT 개발 버전으로 확장되었습니다.

public byte[] sign(InputStream content) throws IOException 
{ 
    ... 
     CMSSignedData signedData = gen.generate(processable, false); 
     // vvv Additional call 
     if (tsaClient != null) 
     { 
      signedData = signTimeStamps(signedData); 
     } 
     // ^^^ Additional call 
     return signedData.getEncoded(); 
    ... 
} 

// vvv Additional helper methods 
private CMSSignedData signTimeStamps(CMSSignedData signedData) 
     throws IOException, TSPException 
{ 
    SignerInformationStore signerStore = signedData.getSignerInfos(); 
    List<SignerInformation> newSigners = new ArrayList<SignerInformation>(); 

    for (SignerInformation signer : (Collection<SignerInformation>)signerStore.getSigners()) 
    { 
     newSigners.add(signTimeStamp(signer)); 
    } 

    return CMSSignedData.replaceSigners(signedData, new SignerInformationStore(newSigners)); 
} 

private SignerInformation signTimeStamp(SignerInformation signer) 
     throws IOException, TSPException 
{ 
    AttributeTable unsignedAttributes = signer.getUnsignedAttributes(); 

    ASN1EncodableVector vector = new ASN1EncodableVector(); 
    if (unsignedAttributes != null) 
    { 
     vector = unsignedAttributes.toASN1EncodableVector(); 
    } 

    byte[] token = tsaClient.getTimeStampToken(signer.getSignature()); 
    ASN1ObjectIdentifier oid = PKCSObjectIdentifiers.id_aa_signatureTimeStampToken; 
    ASN1Encodable signatureTimeStamp = new Attribute(oid, new DERSet(byteToASN1Object(token))); 

    vector.add(signatureTimeStamp); 
    Attributes signedAttributes = new Attributes(vector); 

    SignerInformation newSigner = SignerInformation.replaceUnsignedAttributes(
      signer, new AttributeTable(signedAttributes)); 

    if (newSigner == null) 
    { 
     return signer; 
    } 

    return newSigner; 
} 

private ASN1Object byteToASN1Object(byte[] data) throws IOException 
{ 
    ASN1InputStream in = new ASN1InputStream(data); 
    try 
    { 
     return in.readObject(); 
    } 
    finally 
    { 
     in.close(); 
    } 
} 
// ^^^ Additional helper methods 

(CreateSignature.java 2.0 :

주요 차이점 sign(InputStream)에서 CMS 서명을 생성 한 후, CMS 서명 용기는 또한 서명 타임 스탬프를 전달하는 추가 방법 signTimeStamps(CMSSignedData) 증강된다는 것이다 0.0-SNAPSHOT 개발 버전)

여기

tsaClient 새로운입니다 이러한 추가 만 BouncyCastle에 의존 같이

/** 
* Time Stamping Authority (TSA) Client [RFC 3161]. 
* @author Vakhtang Koroghlishvili 
* @author John Hewson 
*/ 
public class TSAClient 
{ 
    private static final Log log = LogFactory.getLog(TSAClient.class); 

    private final URL url; 
    private final String username; 
    private final String password; 
    private final MessageDigest digest; 

    public TSAClient(URL url, String username, String password, MessageDigest digest) 
    { 
     this.url = url; 
     this.username = username; 
     this.password = password; 
     this.digest = digest; 
    } 

    public byte[] getTimeStampToken(byte[] messageImprint) throws IOException 
    { 
     digest.reset(); 
     byte[] hash = digest.digest(messageImprint); 

     // 32-bit cryptographic nonce 
     SecureRandom random = new SecureRandom(); 
     int nonce = random.nextInt(); 

     // generate TSA request 
     TimeStampRequestGenerator tsaGenerator = new TimeStampRequestGenerator(); 
     tsaGenerator.setCertReq(true); 
     ASN1ObjectIdentifier oid = getHashObjectIdentifier(digest.getAlgorithm()); 
     TimeStampRequest request = tsaGenerator.generate(oid, hash, BigInteger.valueOf(nonce)); 

     // get TSA response 
     byte[] tsaResponse = getTSAResponse(request.getEncoded()); 

     TimeStampResponse response; 
     try 
     { 
      response = new TimeStampResponse(tsaResponse); 
      response.validate(request); 
     } 
     catch (TSPException e) 
     { 
      throw new IOException(e); 
     } 

     TimeStampToken token = response.getTimeStampToken(); 
     if (token == null) 
     { 
      throw new IOException("Response does not have a time stamp token"); 
     } 

     return token.getEncoded(); 
    } 

    // gets response data for the given encoded TimeStampRequest data 
    // throws IOException if a connection to the TSA cannot be established 
    private byte[] getTSAResponse(byte[] request) throws IOException 
    { 
     log.debug("Opening connection to TSA server"); 

     // todo: support proxy servers 
     URLConnection connection = url.openConnection(); 
     connection.setDoOutput(true); 
     connection.setDoInput(true); 
     connection.setRequestProperty("Content-Type", "application/timestamp-query"); 

     log.debug("Established connection to TSA server"); 

     if (username != null && password != null) 
     { 
      if (!username.isEmpty() && !password.isEmpty()) 
      { 
       connection.setRequestProperty(username, password); 
      } 
     } 

     // read response 
     OutputStream output = null; 
     try 
     { 
      output = connection.getOutputStream(); 
      output.write(request); 
     } 
     finally 
     { 
      IOUtils.closeQuietly(output); 
     } 

     log.debug("Waiting for response from TSA server"); 

     InputStream input = null; 
     byte[] response; 
     try 
     { 
      input = connection.getInputStream(); 
      response = IOUtils.toByteArray(input); 
     } 
     finally 
     { 
      IOUtils.closeQuietly(input); 
     } 

     log.debug("Received response from TSA server"); 

     return response; 
    } 

    // returns the ASN.1 OID of the given hash algorithm 
    private ASN1ObjectIdentifier getHashObjectIdentifier(String algorithm) 
    { 
     // TODO can bouncy castle or Java provide this information? 
     if (algorithm.equals("MD2")) 
     { 
      return new ASN1ObjectIdentifier("1.2.840.113549.2.2"); 
     } 
     else if (algorithm.equals("MD5")) 
     { 
      return new ASN1ObjectIdentifier("1.2.840.113549.2.5"); 
     } 
     else if (algorithm.equals("SHA-1")) 
     { 
      return new ASN1ObjectIdentifier("1.3.14.3.2.26"); 
     } 
     else if (algorithm.equals("SHA-224")) 
     { 
      return new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.4"); 
     } 
     else if (algorithm.equals("SHA-256")) 
     { 
      return new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.1"); 
     } 
     else if (algorithm.equals("SHA-394")) 
     { 
      return new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.2"); 
     } 
     else if (algorithm.equals("SHA-512")) 
     { 
      return new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.3"); 
     } 
     else 
     { 
      return new ASN1ObjectIdentifier(algorithm); 
     } 
    } 
} 

(TSAClient.java, 2.0.0-SNAPSHOT 개발 버전)

: 질문 외부 TSA를 인터페이싱 TSAClient 인스턴스를 포함 835,294,066,503,210 멤버 변수 버전이지만 PDFBox 코드는 아니지만이 코드는 PDFBox 1.8.x에서 사용하기 위해 백 포트하기 쉬워야합니다.

+0

당신은 분명히 당신의 byteToASN1Object (byte []) 대신에'ASN1Object.fromByteArray (byte [])'를 사용하기를 원할 것입니다. – divanov

+0

글쎄, 그 입력 PDFBox 개발자의 관심이있을 수 있습니다. – mkl

+0

@divanov 지금은 ASN1Primitive이지만 감사합니다. –