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에서 사용하기 위해 백 포트하기 쉬워야합니다.
출처
2015-01-12 09:28:13
mkl
1) 현재 버전 (1.8.8)으로 업데이트하십시오. PDF의 "구조적"부분에 몇 가지 버그 수정이있었습니다. 2) mkl의 대답이 좋은지 (나는 생각한다), 또는 문제가 있는지/더 이상의 도움이 필요한지 여부를 알려주십시오. –