| /* |
| * Copyright 2003-2006 The Apache Software Foundation, or their licensors, as |
| * appropriate. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.ws.security.processor; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.ws.security.CustomTokenPrincipal; |
| import org.apache.ws.security.WSConstants; |
| import org.apache.ws.security.WSDerivedKeyTokenPrincipal; |
| import org.apache.ws.security.WSDocInfo; |
| import org.apache.ws.security.WSDocInfoStore; |
| import org.apache.ws.security.WSPasswordCallback; |
| import org.apache.ws.security.WSSConfig; |
| import org.apache.ws.security.WSSecurityEngine; |
| import org.apache.ws.security.WSSecurityEngineResult; |
| import org.apache.ws.security.WSSecurityException; |
| import org.apache.ws.security.WSUsernameTokenPrincipal; |
| import org.apache.ws.security.components.crypto.Crypto; |
| import org.apache.ws.security.message.EnvelopeIdResolver; |
| import org.apache.ws.security.message.token.BinarySecurity; |
| import org.apache.ws.security.message.token.DerivedKeyToken; |
| import org.apache.ws.security.message.token.PKIPathSecurity; |
| import org.apache.ws.security.message.token.SecurityTokenReference; |
| import org.apache.ws.security.message.token.UsernameToken; |
| import org.apache.ws.security.message.token.X509Security; |
| import org.apache.ws.security.saml.SAMLKeyInfo; |
| import org.apache.ws.security.saml.SAMLUtil; |
| import org.apache.ws.security.util.WSSecurityUtil; |
| import org.apache.xml.security.exceptions.XMLSecurityException; |
| import org.apache.xml.security.keys.KeyInfo; |
| import org.apache.xml.security.signature.Reference; |
| import org.apache.xml.security.signature.SignedInfo; |
| import org.apache.xml.security.signature.XMLSignature; |
| import org.apache.xml.security.signature.XMLSignatureException; |
| import org.opensaml.SAMLAssertion; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| import javax.security.auth.callback.Callback; |
| import javax.security.auth.callback.CallbackHandler; |
| import javax.xml.namespace.QName; |
| |
| import java.security.Principal; |
| import java.security.cert.CertificateExpiredException; |
| import java.security.cert.CertificateNotYetValidException; |
| import java.security.cert.X509Certificate; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.Vector; |
| |
| public class SignatureProcessor implements Processor { |
| private static Log log = LogFactory.getLog(SignatureProcessor.class.getName()); |
| private static Log tlog = |
| LogFactory.getLog("org.apache.ws.security.TIME"); |
| |
| private String signatureId; |
| |
| public void handleToken(Element elem, Crypto crypto, Crypto decCrypto, CallbackHandler cb, WSDocInfo wsDocInfo, Vector returnResults, WSSConfig wsc) throws WSSecurityException { |
| if (log.isDebugEnabled()) { |
| log.debug("Found signature element"); |
| } |
| WSDocInfoStore.store(wsDocInfo); |
| X509Certificate[] returnCert = new X509Certificate[1]; |
| Set returnElements = new HashSet(); |
| Set protectedElements = new java.util.TreeSet(); |
| byte[][] signatureValue = new byte[1][]; |
| Principal lastPrincipalFound = null; |
| try { |
| lastPrincipalFound = verifyXMLSignature((Element) elem, |
| crypto, returnCert, returnElements, protectedElements, signatureValue, cb); |
| } catch (WSSecurityException ex) { |
| throw ex; |
| } finally { |
| WSDocInfoStore.delete(wsDocInfo); |
| } |
| if (lastPrincipalFound instanceof WSUsernameTokenPrincipal) { |
| returnResults.add(0, new WSSecurityEngineResult( |
| WSConstants.UT_SIGN, lastPrincipalFound, null, |
| returnElements, protectedElements, signatureValue[0])); |
| } else { |
| returnResults.add(0, new WSSecurityEngineResult( |
| WSConstants.SIGN, lastPrincipalFound, |
| returnCert[0], returnElements, protectedElements, signatureValue[0])); |
| } |
| signatureId = elem.getAttributeNS(null, "Id"); |
| } |
| |
| /** |
| * Verify the WS-Security signature. |
| * <p/> |
| * The functions at first checks if then <code>KeyInfo</code> that is |
| * contained in the signature contains standard X509 data. If yes then |
| * get the certificate data via the standard <code>KeyInfo</code> methods. |
| * <p/> |
| * Otherwise, if the <code>KeyInfo</code> info does not contain X509 data, check |
| * if we can find a <code>wsse:SecurityTokenReference</code> element. If yes, the next |
| * step is to check how to get the certificate. Two methods are currently supported |
| * here: |
| * <ul> |
| * <li> A URI reference to a binary security token contained in the <code>wsse:Security |
| * </code> header. If the dereferenced token is |
| * of the correct type the contained certificate is extracted. |
| * </li> |
| * <li> Issuer name an serial number of the certificate. In this case the method |
| * looks up the certificate in the keystore via the <code>crypto</code> parameter. |
| * </li> |
| * </ul> |
| * <p/> |
| * The methods checks is the certificate is valid and calls the |
| * {@link org.apache.xml.security.signature.XMLSignature#checkSignatureValue(X509Certificate) |
| * verification} function. |
| * |
| * @param elem the XMLSignature DOM Element. |
| * @param crypto the object that implements the access to the keystore and the |
| * handling of certificates. |
| * @param returnCert verifyXMLSignature stores the certificate in the first |
| * entry of this array. Ther caller may then further validate |
| * the certificate |
| * @param returnElements verifyXMLSignature adds the wsu:ID attribute values for |
| * the signed elements to this Set |
| * @param cb CallbackHandler instance to extract key passwords |
| * @return the subject principal of the validated X509 certificate (the |
| * authenticated subject). The calling function may use this |
| * principal for further authentication or authorization. |
| * @throws WSSecurityException |
| */ |
| protected Principal verifyXMLSignature(Element elem, |
| Crypto crypto, |
| X509Certificate[] returnCert, |
| Set returnElements, |
| Set protectedElements, |
| byte[][] signatureValue, |
| CallbackHandler cb) |
| throws WSSecurityException { |
| if (log.isDebugEnabled()) { |
| log.debug("Verify XML Signature"); |
| } |
| long t0 = 0, t1 = 0, t2 = 0; |
| if (tlog.isDebugEnabled()) { |
| t0 = System.currentTimeMillis(); |
| } |
| |
| XMLSignature sig = null; |
| try { |
| sig = new XMLSignature(elem, null); |
| } catch (XMLSecurityException e2) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK, "noXMLSig", null, e2 |
| ); |
| } |
| |
| sig.addResourceResolver(EnvelopeIdResolver.getInstance()); |
| |
| X509Certificate[] certs = null; |
| KeyInfo info = sig.getKeyInfo(); |
| byte[] secretKey = null; |
| UsernameToken ut = null; |
| DerivedKeyToken dkt = null; |
| SAMLKeyInfo samlKi = null; |
| String customTokenId = null; |
| |
| if (info != null) { |
| Node node = WSSecurityUtil.getDirectChild(info.getElement(), |
| SecurityTokenReference.SECURITY_TOKEN_REFERENCE, |
| WSConstants.WSSE_NS); |
| if (node == null) { |
| throw new WSSecurityException( |
| WSSecurityException.INVALID_SECURITY, |
| "unsupportedKeyInfo"); |
| } |
| SecurityTokenReference secRef = new SecurityTokenReference((Element) node); |
| |
| int docHash = elem.getOwnerDocument().hashCode(); |
| /* |
| * Here we get some information about the document that is being |
| * processed, in particular the crypto implementation, and already |
| * detected BST that may be used later during dereferencing. |
| */ |
| WSDocInfo wsDocInfo = WSDocInfoStore.lookup(docHash); |
| |
| if (secRef.containsReference()) { |
| Element token = secRef.getTokenElement(elem.getOwnerDocument(), |
| wsDocInfo, cb); |
| /* |
| * at this point check token type: UsernameToken, Binary, SAML |
| * Crypto required only for Binary and SAML |
| */ |
| QName el = new QName(token.getNamespaceURI(), token |
| .getLocalName()); |
| if (el.equals(WSSecurityEngine.usernameToken)) { |
| String id = token.getAttributeNS(WSConstants.WSU_NS, "Id"); |
| UsernameTokenProcessor utProcessor = |
| (UsernameTokenProcessor) wsDocInfo.getProcessor(id); |
| ut = utProcessor.getUt(); |
| secretKey = ut.getSecretKey(); |
| } else if(el.equals(WSSecurityEngine.DERIVED_KEY_TOKEN_05_02) || |
| el.equals(WSSecurityEngine.DERIVED_KEY_TOKEN_05_12)) { |
| dkt = new DerivedKeyToken(token); |
| String id = dkt.getID(); |
| DerivedKeyTokenProcessor dktProcessor = |
| (DerivedKeyTokenProcessor) wsDocInfo.getProcessor(id); |
| String signatureMethodURI = sig.getSignedInfo().getSignatureMethodURI(); |
| int keyLength = (dkt.getLength() > 0) ? dkt.getLength() : |
| WSSecurityUtil.getKeyLength(signatureMethodURI); |
| |
| secretKey = dktProcessor.getKeyBytes(keyLength); |
| } else { |
| if (el.equals(WSSecurityEngine.binaryToken)) { |
| //TODO: Use results from BinarySecurityTokenProcessor |
| certs = getCertificatesTokenReference((Element) token, |
| crypto); |
| } else if (el.equals(WSSecurityEngine.SAML_TOKEN)) { |
| if (crypto == null) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noSigCryptoFile"); |
| } |
| samlKi = SAMLUtil.getSAMLKeyInfo( |
| (Element) token, crypto, cb); |
| certs = samlKi.getCerts(); |
| secretKey = samlKi.getSecret(); |
| |
| } else if (el.equals(WSSecurityEngine.ENCRYPTED_KEY)){ |
| String encryptedKeyID = token.getAttributeNS(null,"Id"); |
| EncryptedKeyProcessor encryptKeyProcessor = (EncryptedKeyProcessor) |
| wsDocInfo.getProcessor(encryptedKeyID); |
| |
| if (encryptKeyProcessor == null ) { |
| if (crypto == null) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noSigCryptoFile"); |
| } |
| encryptKeyProcessor = new EncryptedKeyProcessor(); |
| encryptKeyProcessor.handleEncryptedKey((Element)token, cb, crypto); |
| |
| } |
| secretKey = encryptKeyProcessor.getDecryptedBytes(); |
| |
| } else { |
| |
| //Try custom token through callback handler |
| //try to find a custom token |
| String id = secRef |
| .getReference().getURI().substring(1); |
| WSPasswordCallback pwcb = new WSPasswordCallback(id, |
| WSPasswordCallback.CUSTOM_TOKEN); |
| try { |
| cb.handle(new Callback[]{pwcb}); |
| } catch (Exception e) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noPassword", new Object[] { id }, e); |
| } |
| |
| secretKey = pwcb.getKey(); |
| customTokenId = id; |
| |
| if(secretKey == null) { |
| throw new WSSecurityException( |
| WSSecurityException.INVALID_SECURITY, |
| "unsupportedKeyInfo", new Object[]{el |
| .toString()}); |
| } |
| } |
| } |
| } else if (secRef.containsX509Data() || secRef.containsX509IssuerSerial()) { |
| certs = secRef.getX509IssuerSerial(crypto); |
| } else if (secRef.containsKeyIdentifier()) { |
| if (secRef.getKeyIdentifierValueType().equals |
| (SecurityTokenReference.ENC_KEY_SHA1_URI)) { |
| |
| String id = secRef.getKeyIdentifierValue(); |
| WSPasswordCallback pwcb = new WSPasswordCallback(id, |
| WSPasswordCallback.ENCRYPTED_KEY_TOKEN); |
| try { |
| cb.handle(new Callback[]{pwcb}); |
| } catch (Exception e) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noPassword", new Object[] { id }, e); |
| } |
| secretKey = pwcb.getKey(); |
| |
| } else { |
| certs = secRef.getKeyIdentifier(crypto); |
| } |
| } else { |
| throw new WSSecurityException( |
| WSSecurityException.INVALID_SECURITY, |
| "unsupportedKeyInfo", new Object[]{node.toString()}); |
| } |
| } else { |
| if (crypto == null) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noSigCryptoFile"); |
| } |
| if (crypto.getDefaultX509Alias() != null) { |
| certs = crypto.getCertificates(crypto.getDefaultX509Alias()); |
| } else { |
| throw new WSSecurityException( |
| WSSecurityException.INVALID_SECURITY, |
| "unsupportedKeyInfo"); |
| } |
| } |
| if (tlog.isDebugEnabled()) { |
| t1 = System.currentTimeMillis(); |
| } |
| if ((certs == null || certs.length == 0 || certs[0] == null) && secretKey == null) { |
| throw new WSSecurityException(WSSecurityException.FAILED_CHECK); |
| } |
| if (certs != null) { |
| try { |
| certs[0].checkValidity(); |
| } catch (CertificateExpiredException e) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK, "invalidCert", null, e |
| ); |
| } catch (CertificateNotYetValidException e) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK, "invalidCert", null, e |
| ); |
| } |
| } |
| try { |
| boolean signatureOk = false; |
| if (certs != null) { |
| signatureOk = sig.checkSignatureValue(certs[0]); |
| } else { |
| signatureOk = sig.checkSignatureValue(sig |
| .createSecretKey(secretKey)); |
| } |
| if (signatureOk) { |
| if (tlog.isDebugEnabled()) { |
| t2 = System.currentTimeMillis(); |
| tlog.debug("Verify: total= " + (t2 - t0) |
| + ", prepare-cert= " + (t1 - t0) + ", verify= " |
| + (t2 - t1)); |
| } |
| signatureValue[0] = sig.getSignatureValue(); |
| /* |
| * Now dig into the Signature element to get the elements that |
| * this Signature covers. Build the QName of these Elements and |
| * return them to caller |
| */ |
| SignedInfo si = sig.getSignedInfo(); |
| int numReferences = si.getLength(); |
| for (int i = 0; i < numReferences; i++) { |
| Reference siRef; |
| try { |
| siRef = si.item(i); |
| } catch (XMLSecurityException e3) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK, null, null, e3 |
| ); |
| } |
| String uri = siRef.getURI(); |
| if(uri != null && !"".equals(uri)) { |
| Element se = WSSecurityUtil.getElementByWsuId(elem.getOwnerDocument(), uri); |
| if (se == null) { |
| se = WSSecurityUtil.getElementByGenId(elem |
| .getOwnerDocument(), uri); |
| } |
| if (se == null) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK); |
| } |
| returnElements.add(WSSecurityUtil.getIDFromReference(uri)); |
| } else { |
| //This is the case where the signed element is identified |
| //by a transform such as XPath filtering |
| //We add the complete reference element to the return |
| //elements |
| returnElements.add(siRef); |
| } |
| } |
| |
| if (certs != null) { |
| returnCert[0] = certs[0]; |
| return certs[0].getSubjectDN(); |
| } else if(ut != null){ |
| WSUsernameTokenPrincipal principal = new WSUsernameTokenPrincipal( |
| ut.getName(), ut.isHashed()); |
| principal.setNonce(ut.getNonce()); |
| principal.setPassword(ut.getPassword()); |
| principal.setCreatedTime(ut.getCreated()); |
| return principal; |
| } else if (dkt != null) { |
| WSDerivedKeyTokenPrincipal principal = new WSDerivedKeyTokenPrincipal(dkt.getID()); |
| principal.setNonce(dkt.getNonce()); |
| principal.setLabel(dkt.getLabel()); |
| principal.setLength(dkt.getLength()); |
| principal.setOffset(dkt.getOffset()); |
| String basetokenId = null; |
| SecurityTokenReference securityTokenReference = dkt |
| .getSecurityTokenReference(); |
| if (securityTokenReference.containsReference()) { |
| basetokenId = securityTokenReference.getReference() |
| .getURI().substring(1); |
| } else { |
| // KeyIdentifier |
| basetokenId = securityTokenReference |
| .getKeyIdentifierValue(); |
| } |
| principal.setBasetokenId(basetokenId); |
| return principal; |
| } else if(samlKi != null) { |
| final SAMLAssertion assertion = samlKi.getAssertion(); |
| CustomTokenPrincipal principal = new CustomTokenPrincipal(assertion.getId()); |
| principal.setTokenObject(assertion); |
| return principal; |
| } else if(secretKey != null) { |
| //This is the custom key scenario |
| CustomTokenPrincipal principal = new CustomTokenPrincipal(customTokenId); |
| return principal; |
| } else { |
| throw new WSSecurityException("Cannot determine principal"); |
| } |
| } else { |
| throw new WSSecurityException(WSSecurityException.FAILED_CHECK); |
| } |
| } catch (XMLSignatureException e1) { |
| throw new WSSecurityException( |
| WSSecurityException.FAILED_CHECK, null, null, e1 |
| ); |
| } |
| } |
| |
| /** |
| * Extracts the certificate(s) from the Binary Security token reference. |
| * <p/> |
| * |
| * @param elem The element containing the binary security token. This is |
| * either X509 certificate(s) or a PKIPath. |
| * @return an array of X509 certificates |
| * @throws WSSecurityException |
| */ |
| public X509Certificate[] getCertificatesTokenReference(Element elem, |
| Crypto crypto) |
| throws WSSecurityException { |
| if (crypto == null) { |
| throw new WSSecurityException(WSSecurityException.FAILURE, |
| "noSigCryptoFile"); |
| } |
| BinarySecurity token = createSecurityToken(elem); |
| if (token instanceof PKIPathSecurity) { |
| return ((PKIPathSecurity) token).getX509Certificates(false, crypto); |
| } else if (token instanceof X509Security) { |
| X509Certificate cert = ((X509Security) token).getX509Certificate(crypto); |
| X509Certificate[] certs = new X509Certificate[1]; |
| certs[0] = cert; |
| return certs; |
| } |
| return null; |
| } |
| |
| /** |
| * Checks the <code>element</code> and creates appropriate binary security object. |
| * |
| * @param element The XML element that contains either a <code>BinarySecurityToken |
| * </code> or a <code>PKIPath</code> element. Other element types a not |
| * supported |
| * @return the BinarySecurity object, either a <code>X509Security</code> or a |
| * <code>PKIPathSecurity</code> object. |
| * @throws WSSecurityException |
| */ |
| private BinarySecurity createSecurityToken(Element element) throws WSSecurityException { |
| BinarySecurity token = new BinarySecurity(element); |
| String type = token.getValueType(); |
| X509Security x509 = null; |
| PKIPathSecurity pkiPath = null; |
| |
| if (X509Security.X509_V3_TYPE.equals(type)) { |
| x509 = new X509Security(element); |
| return (BinarySecurity) x509; |
| } else if (X509Security.X509_V1_TYPE.equals(type)) { |
| x509 = new X509Security(element); |
| return (BinarySecurity) x509; |
| } else if (PKIPathSecurity.getType().equals(type)) { |
| pkiPath = new PKIPathSecurity(element); |
| return (BinarySecurity) pkiPath; |
| } |
| throw new WSSecurityException(WSSecurityException.UNSUPPORTED_SECURITY_TOKEN, |
| "unsupportedBinaryTokenType", new Object[]{type}); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.ws.security.processor.Processor#getId() |
| */ |
| public String getId() { |
| return signatureId; |
| } |
| |
| } |