View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.wss4j.stax.impl.securityToken;
20  
21  import java.security.Key;
22  import java.security.Principal;
23  import java.security.cert.X509Certificate;
24  import java.util.Collection;
25  import java.util.regex.Matcher;
26  import java.util.regex.Pattern;
27  
28  import javax.security.auth.Subject;
29  import javax.security.auth.callback.CallbackHandler;
30  
31  import org.apache.wss4j.common.crypto.Crypto;
32  import org.apache.wss4j.common.crypto.CryptoType;
33  import org.apache.wss4j.common.ext.WSPasswordCallback;
34  import org.apache.wss4j.common.ext.WSSecurityException;
35  import org.apache.wss4j.common.principal.PublicKeyPrincipalImpl;
36  import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
37  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
38  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
39  import org.apache.wss4j.stax.securityToken.X509SecurityToken;
40  import org.apache.wss4j.stax.utils.WSSUtils;
41  import org.apache.xml.security.exceptions.XMLSecurityException;
42  import org.apache.xml.security.stax.ext.XMLSecurityConstants;
43  import org.apache.xml.security.stax.securityToken.SecurityTokenConstants.TokenType;
44  
45  public abstract class X509SecurityTokenImpl
46          extends org.apache.xml.security.stax.impl.securityToken.X509SecurityToken implements X509SecurityToken {
47  
48      private static final transient org.slf4j.Logger LOG =
49          org.slf4j.LoggerFactory.getLogger(X509SecurityTokenImpl.class);
50  
51      private CallbackHandler callbackHandler;
52      private Crypto crypto;
53      private WSSSecurityProperties securityProperties;
54      private Principal principal;
55  
56      protected X509SecurityTokenImpl(
57              WSSecurityTokenConstants.TokenType tokenType, WSInboundSecurityContext wsInboundSecurityContext,
58              Crypto crypto, CallbackHandler callbackHandler, String id,
59              WSSecurityTokenConstants.KeyIdentifier keyIdentifier, WSSSecurityProperties securityProperties,
60              boolean includedInMessage) {
61          super(tokenType, wsInboundSecurityContext, id, keyIdentifier, includedInMessage);
62          this.crypto = crypto;
63          this.callbackHandler = callbackHandler;
64          this.securityProperties = securityProperties;
65      }
66  
67      protected Crypto getCrypto() {
68          return crypto;
69      }
70  
71      protected void setCrypto(Crypto crypto) {
72          this.crypto = crypto;
73      }
74  
75      public CallbackHandler getCallbackHandler() {
76          return callbackHandler;
77      }
78  
79      @Override
80      public Key getKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage,
81                        String correlationID) throws XMLSecurityException {
82          WSPasswordCallback pwCb = new WSPasswordCallback(getAlias(), WSPasswordCallback.DECRYPT);
83          WSSUtils.doPasswordCallback(getCallbackHandler(), pwCb);
84          try {
85              return getCrypto().getPrivateKey(getAlias(), pwCb.getPassword());
86          } catch (WSSecurityException ex) {
87              // Check to see if we are decrypting rather than signature verification
88              Crypto decCrypto = securityProperties.getDecryptionCrypto();
89              if (decCrypto != null && decCrypto != getCrypto()) {
90                  return decCrypto.getPrivateKey(getAlias(), pwCb.getPassword());
91              }
92              throw ex;
93          }
94      }
95  
96      @Override
97      public X509Certificate[] getX509Certificates() throws XMLSecurityException {
98          if (super.getX509Certificates() == null) {
99              String alias = getAlias();
100             if (alias != null) {
101                 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
102                 cryptoType.setAlias(alias);
103                 setX509Certificates(getCrypto().getX509Certificates(cryptoType));
104             }
105         }
106         return super.getX509Certificates();
107     }
108 
109     @Override
110     public void verify() throws XMLSecurityException {
111         //todo overall call verify on wrapping tokens for non top-level SecurityTokens!?
112         X509Certificate[] x509Certificates = getX509Certificates();
113         if (x509Certificates != null && x509Certificates.length > 0) {
114             boolean enableRevocation = false;
115             Collection<Pattern> subjectCertConstraints = null;
116             Collection<Pattern> issuerCertConstraints = null;
117             if (securityProperties != null) {
118                 enableRevocation = securityProperties.isEnableRevocation();
119                 subjectCertConstraints = securityProperties.getSubjectCertConstraints();
120                 issuerCertConstraints = securityProperties.getIssuerDNConstraints();
121             }
122             getCrypto().verifyTrust(x509Certificates, enableRevocation, subjectCertConstraints, issuerCertConstraints);
123         }
124     }
125 
126     /**
127      * @return      true if the certificate's SubjectDN matches the constraints defined in the
128      *              subject DNConstraints; false, otherwise. The certificate subject DN only
129      *              has to match ONE of the subject cert constraints (not all).
130      */
131     protected boolean
132     matches(
133         final X509Certificate cert, final Collection<Pattern> subjectDNPatterns
134     ) {
135         if (subjectDNPatterns.isEmpty()) {
136             LOG.warn("No Subject DN Certificate Constraints were defined. This could be a security issue");
137         }
138         if (!subjectDNPatterns.isEmpty()) {
139             if (cert == null) {
140                 LOG.debug("The certificate is null so no constraints matching was possible");
141                 return false;
142             }
143             String subjectName = cert.getSubjectX500Principal().getName();
144             boolean subjectMatch = false;
145             for (Pattern subjectDNPattern : subjectDNPatterns) {
146                 final Matcher matcher = subjectDNPattern.matcher(subjectName);
147                 if (matcher.matches()) {
148                     LOG.debug("Subject DN {} matches with pattern {}", subjectName, subjectDNPattern);
149                     subjectMatch = true;
150                     break;
151                 }
152             }
153             if (!subjectMatch) {
154                 return false;
155             }
156         }
157 
158         return true;
159     }
160 
161     protected abstract String getAlias() throws XMLSecurityException;
162 
163     @Override
164     public Subject getSubject() throws WSSecurityException {
165         return null;
166     }
167 
168     @Override
169     public Principal getPrincipal() throws WSSecurityException {
170         if (this.principal == null) {
171             try {
172                 X509Certificate[] certs = getX509Certificates();
173                 if (certs != null && certs.length > 0) {
174                     return this.principal = certs[0].getSubjectX500Principal();
175                 }
176                 return this.principal = new PublicKeyPrincipalImpl(getPublicKey());
177             } catch (XMLSecurityException e) {
178                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, e);
179             }
180         }
181         return this.principal;
182     }
183 
184     @Override
185     public TokenType getTokenType() {
186         TokenType storedTokenType = super.getTokenType();
187         // Just check to see whether the cert version is "1"
188         if (WSSecurityTokenConstants.X509V3Token.equals(storedTokenType)) {
189             try {
190                 X509Certificate[] certs = super.getX509Certificates();
191                 if (certs != null && certs.length > 0 && certs[0].getVersion() == 1) {
192                     return WSSecurityTokenConstants.X509V1Token;
193                 }
194             } catch (XMLSecurityException e) {
195                 return storedTokenType;
196             }
197         }
198 
199         return storedTokenType;
200 
201 
202     }
203 }