CreateXMLSignatureRequest
.
*
* @author mcentner
*/
public class Signature {
public static final String XMLDSIG_PREFIX = "dsig";
/**
* Logging facility.
*/
private static Log log = LogFactory.getLog(Signature.class);
/**
* The DOM implementation used.
*/
private DOMImplementationLS domImplLS;
/**
* The SignatureContext for the XMLSignature.
*/
private SignatureContext ctx;
/**
* The list of {@link DataObject}s for this signature.
*/
private ListId
-attribute values of this signature's
* ds:Reference
s to the corresponding {@link DataObject}s.
*/
// private Mapxsd:ID
to be registered in the {@link DOMSignContext}.
*/
private ListsignatureInfo
provided.
*
* @param signatureInfo
* the SignatureInfo
*
* @throws SLCommandException
* if processing fails for any reason
* @throws IllegalStateException
* if the parent
node has already been set
* @throws NullPointerException
* if signatureInfo
is null
*/
public void setSignatureInfo(SignatureInfoCreationType signatureInfo) throws SLCommandException {
if (signatureLocation != null) {
throw new IllegalStateException("SignatureEnvironment already set.");
}
Base64XMLOptRefContentType signatureEnvironment = signatureInfo.getSignatureEnvironment();
if (signatureEnvironment == null) {
// no SignatureEnvironment, so we use an empty document and the document as parent
ensureSignatureLocation();
} else {
// parse SignatureEnvrionment and use as document
Document document = parseSignatureEnvironment(signatureEnvironment, signatureInfo.getSupplement());
ctx.setDocument(document);
signatureLocation = new SignatureLocation(ctx);
signatureLocation.setSignatureInfo(signatureInfo);
}
}
/**
* Ensures a SignatureLocation for this Signature.
*/
private void ensureSignatureLocation() {
if (signatureLocation == null) {
Document document = DOMUtils.createDocument();
ctx.setDocument(document);
signatureLocation = new SignatureLocation(ctx);
signatureLocation.setParent(document);
}
}
/**
* Adds a DataObject with the information given by the
* dataObjectInfo
provided to this Signature.
*
* @param dataObjectInfo
* the DataObjectInfo
element
*
* @throws SLCommandException
* if adding the DataObject fails
* @throws SLRequestException
* if the information provided by the given
* dataObjectInfo
does not conform to the security
* layer specification
* @throws NullPointerException
* if dataObjectInfo
is null
*/
public void addDataObject(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
ensureSignatureLocation();
DataObject dataObject = new DataObject(ctx);
dataObject.setDataObjectInfo(dataObjectInfo);
dataObjects.add(dataObject);
// dataObjectReferencIds.put(dataObject.getReference().getId(), dataObject);
}
/**
* Sets the SigningTime
qualifying property of this Signature.
*
* @param signingTime the signing time to set
*/
public void setSigningTime(Date signingTime) {
this.signingTime = signingTime;
}
/**
* Sets the SignerCertificate
qualifying property of this Signature.
*
* @param certificate the signer's certificate
*/
public void setSignerCeritifcate(X509Certificate certificate) {
this.signerCertificate = certificate;
}
/**
* Builds the XMLSignature data structure of this Signature as configured by
* the various setter methods.
*
* @throws SLCommandException if building this signature fails
*/
public void buildXMLSignature() throws SLCommandException {
String signatureId = ctx.getIdValueFactory().createIdValue("Signature");
ListsignContext
.
* * Call's {@link #buildXMLSignature()} if it has not been called yet. *
* * @param signContext * the signing context * * @throws MarshalException * if marshalling the XMLSignature fails * @throws XMLSignatureException * if signing the XMLSignature fails * @throws SLCommandException * if building the XMLSignature fails * @throws SLViewerException * @throws NullPointerException * ifsignContext
is null
*/
public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException {
if (xmlSignature == null) {
buildXMLSignature();
}
for (IdAttribute idAttribute : idAttributes) {
signContext.setIdAttributeNS(idAttribute.element, idAttribute.namespaceURI, idAttribute.localName);
}
// DO NOT USE:
// signContext.setProperty("iaik.xml.crypto.dsig.sign-over", Boolean.TRUE);
signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
signContext.putNamespacePrefix(XMLSignature.XMLNS,XMLDSIG_PREFIX);
signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext()));
try {
xmlSignature.sign(signContext);
} catch (XMLSignatureException e) {
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof STALSignatureException) {
if (((STALSignatureException) cause).getCause() instanceof SLViewerException) {
throw (SLViewerException) ((STALSignatureException) cause).getCause();
}
int errorCode = ((STALSignatureException) cause).getErrorCode();
SLCommandException commandException = new SLCommandException(errorCode);
log.info("Failed to sign signature.", commandException);
throw commandException;
} else {
cause = cause.getCause();
}
}
throw e;
}
// debug
if (log.isTraceEnabled()) {
for (DataObject dataObject : dataObjects) {
Reference reference = dataObject.getReference();
InputStream digestInputStream = reference.getDigestInputStream();
if (digestInputStream != null) {
String mimeType = dataObject.getMimeType();
StringBuilder sb = new StringBuilder();
sb.append("DigestInput for Reference with id='");
sb.append(reference.getId());
sb.append("' (MIME-Type=");
sb.append(dataObject.getMimeType());
sb.append("):\n");
try {
if (mimeType != null && (
mimeType.startsWith("text") ||
"application/xhtml+xml".equals(mimeType))) {
byte[] b = new byte[512];
for (int l; (l = digestInputStream.read(b)) != -1;) {
sb.append(new String(b, 0, l));
}
} else {
sb.append(HexDump.hexDump(digestInputStream));
}
} catch (IOException e) {
log.error(e);
}
log.trace(sb.toString());
} else {
log.trace("Reference caching is not enabled.");
}
}
for (Reference reference : getReferences()) {
if (reference.getType() != null) {
InputStream digestInputStream = reference.getDigestInputStream();
if (digestInputStream != null) {
StringBuilder sb = new StringBuilder();
sb.append("DigestInput for Reference with id='");
sb.append(reference.getId());
sb.append("'; Type:");
sb.append(reference.getType());
sb.append("):\n");
try {
byte[] b = new byte[512];
for (int l; (l = digestInputStream.read(b)) != -1;) {
sb.append(new String(b, 0, l));
}
} catch (IOException e) {
log.error(e);
}
log.trace(sb.toString());
} else {
log.trace("Reference caching is not enabled.");
}
}
}
}
}
/**
* Sign this Signature using the given stal
implementation and
* keyboxIdentifier
.
* * This method configures an appropriate {@link DOMSignContext} and calls * {@link #sign(DOMSignContext)}. If {@link #buildXMLSignature()} has not been * called yet, it is called by this method. *
* * @param stal * the STAL implementation to use * @param keyboxIdentifier * the KeyboxIdentifier to use * * @throws MarshalException * if marshalling this Signature fails * @throws XMLSignatureException * if signing this Signature fails * @throws SLCommandException * if building this Signature fails * @throws SLViewerException * @throws NullPointerException * ifstal
or keyboxIdentifier
is
* null
*/
public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException {
if (stal == null) {
throw new NullPointerException("Argument 'stal' must not be null.");
}
if (keyboxIdentifier == null) {
throw new NullPointerException("Argument 'keyboxIdentifier' must not be null.");
}
if (xmlSignature == null) {
buildXMLSignature();
}
SignatureMethod signatureMethod = xmlSignature.getSignedInfo().getSignatureMethod();
String algorithm = signatureMethod.getAlgorithm();
//don't get hashDataInputs (digestInputStreams) now, only once Signature.sign() was called (cf STALSignature.engineSign)
PrivateKey privateKey = new STALPrivateKey(stal, algorithm, keyboxIdentifier, dataObjects); // hashDataInputs);
DOMSignContext signContext;
if (getNextSibling() == null) {
signContext = new DOMSignContext(privateKey, getParent());
} else {
signContext = new DOMSignContext(privateKey, getParent(), getNextSibling());
}
sign(signContext);
}
// @Override
// public HashDataInput getHashDataInput(final String referenceId) {
// final DataObject dataObject = dataObjectReferencIds.get(referenceId);
// if (dataObject != null) {
// return new HashDataInput() {
//
// InputStream hashDataInput = dataObject.getReference().getDigestInputStream();
//
// @Override
// public String getReferenceId() {
// return referenceId;
// }
//
// @Override
// public String getMimeType() {
// return dataObject.getMimeType();
// }
//
// @Override
// public InputStream getHashDataInput() {
// return hashDataInput;
// }
// };
// }
// return null;
// }
/**
* Adds the XAdES QualifyingProperties
as an
* ds:Object
and a corresponding ds:Reference
to
* it's SignedProperties
element to this Signature.
*
* @param objects
* the list of ds:Objects
to add the created
* ds:Object
to
* @param references
* the list of ds:References
to add the created
* ds:Reference
to
* @param signatureId TODO
* @throws SLCommandException
* if creating and adding the XAdES
* QualifyingProperties
fails
* @throws NullPointerException
* if objects
or references
is
* null
*/
private void addXAdESObjectAndReference(ListSignatureEnvironment
element
* @param supplements
* an optional list of Supplements
(may be
* null
)
*
* @return the parsed SignatureEnvironment document
*
* @throws SLCommandException
* if parsing the SignatureEnvironment fails
* @throws NullPointerException
* if signatureEnvironment
is null
*/
private Document parseSignatureEnvironment(
Base64XMLOptRefContentType signatureEnvironment,
Listreference
URI.
*
* @param reference
* the reference URL
*
* @return an LSInput from the given reference
URI
*
* @throws IOException
* if dereferencing the given reference
fails
*/
private LSInput createLSInput(String reference) throws IOException {
URLDereferencer urlDereferencer = URLDereferencer.getInstance();
StreamData streamData = urlDereferencer.dereference(reference, ctx.getDereferencerContext());
String contentType = streamData.getContentType();
String charset = HttpUtil.getCharset(contentType, true);
InputStreamReader streamReader;
try {
streamReader = new InputStreamReader(streamData.getStream(), charset);
} catch (UnsupportedEncodingException e) {
log.info("Charset " + charset + " not supported. Using default.");
streamReader = new InputStreamReader(streamData.getStream());
}
LSInput input = domImplLS.createLSInput();
input = domImplLS.createLSInput();
input.setCharacterStream(streamReader);
return input;
}
/**
* Creates an LSInput from the given content
bytes.
*
* @param content
* the content bytes
*
* @return an LSInput from the givne content
bytes
*/
private LSInput createLSInput(byte[] content) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
LSInput input = domImplLS.createLSInput();
input.setByteStream(inputStream);
return input;
}
/**
* Creates an LSInput from the given XML content
.
*
* @param content
* the XML content
* @return an LSInput from the given XML content
*
* @throws XMLStreamException
* if reading the XMLStream from the given XML content fails
*/
private LSInput createLSInput(XMLContentType content) throws XMLStreamException {
ByteArrayOutputStream redirectedStream = content.getRedirectedStream();
if (redirectedStream != null) {
LSInput input = domImplLS.createLSInput();
input.setByteStream(new ByteArrayInputStream(redirectedStream.toByteArray()));
return input;
} else {
return null;
}
}
/**
* Represents an xsd:Id
-attribute value.
*
* @author mcentner
*/
private class IdAttribute {
private Element element;
private String namespaceURI;
private String localName;
}
/**
* An implementation of the LSResourceResolver that uses a list of supplements
* to resolve resources.
*
* @author mcentner
*/
private class LSResourceResolverAdapter implements LSResourceResolver {
List