View Javadoc

1   /*
2    * Licensed to the University Corporation for Advanced Internet Development, 
3    * Inc. (UCAID) under one or more contributor license agreements.  See the 
4    * NOTICE file distributed with this work for additional information regarding
5    * copyright ownership. The UCAID licenses this file to You under the Apache 
6    * License, Version 2.0 (the "License"); you may not use this file except in 
7    * compliance with the License.  You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package edu.internet2.middleware.shibboleth.common.config.security;
19  
20  import java.security.cert.CRLException;
21  import java.security.cert.CertificateException;
22  import java.security.cert.X509CRL;
23  import java.security.cert.X509Certificate;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.xml.namespace.QName;
30  
31  import org.opensaml.xml.security.x509.X509Util;
32  import org.opensaml.xml.util.DatatypeHelper;
33  import org.opensaml.xml.util.XMLHelper;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  import org.springframework.beans.FatalBeanException;
37  import org.springframework.beans.factory.support.AbstractBeanDefinition;
38  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
39  import org.springframework.beans.factory.xml.ParserContext;
40  import org.w3c.dom.Attr;
41  import org.w3c.dom.Element;
42  
43  /**
44   * Base class for X509 credential beans.
45   */
46  public abstract class AbstractX509CredentialBeanDefinitionParser extends AbstractCredentialBeanDefinitionParser {
47  
48      /** Class logger. */
49      private final Logger log = LoggerFactory.getLogger(AbstractX509CredentialBeanDefinitionParser.class);
50  
51      /** {@inheritDoc} */
52      protected Class getBeanClass(Element element) {
53          return X509CredentialFactoryBean.class;
54      }
55  
56      /** {@inheritDoc} */
57      protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
58          return element.getAttributeNS(null, "id");
59      }
60  
61      /** {@inheritDoc} */
62      protected void doParse(Element element, BeanDefinitionBuilder builder) {
63          log.info("Parsing configuration for {} credential with id: {}", XMLHelper.getXSIType(element)
64                  .getLocalPart(), element.getAttributeNS(null, "id"));
65  
66          parseAttributes(element, builder);
67  
68          Map<QName, List<Element>> configChildren = XMLHelper.getChildElements(element);
69  
70          parseCommon(configChildren, builder);
71  
72          parsePrivateKey(configChildren, builder);
73          parseCertificates(configChildren, builder);
74          parseCRLs(configChildren, builder);
75      }
76  
77      /**
78       * Parses the certificates from the credential configuration.
79       * 
80       * @param configChildren children of the credential element
81       * @param builder credential build
82       */
83      protected void parseCertificates(Map<QName, List<Element>> configChildren, BeanDefinitionBuilder builder) {
84          List<Element> certElems = configChildren.get(new QName(SecurityNamespaceHandler.NAMESPACE, "Certificate"));
85          if (certElems == null || certElems.isEmpty()) {
86              return;
87          }
88  
89          log.debug("Parsing x509 credential certificates");
90          ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>();
91          byte[] encodedCert;
92          Collection<X509Certificate> decodedCerts;
93          for (Element certElem : certElems) {
94              encodedCert = getEncodedCertificate(DatatypeHelper.safeTrimOrNullString(certElem.getTextContent()));
95              if (encodedCert == null) {
96                  continue;
97              }
98  
99              boolean isEntityCert = false;
100             Attr entityCertAttr = certElem.getAttributeNodeNS(null, "entityCertificate");
101             if (entityCertAttr != null) {
102                 isEntityCert = XMLHelper.getAttributeValueAsBoolean(entityCertAttr);
103             }
104             if (isEntityCert) {
105                 log.debug("Element config flag found indicating entity certificate");
106             }
107 
108             try {
109                 decodedCerts = X509Util.decodeCertificate(encodedCert);
110                 certs.addAll(decodedCerts);
111                 if (isEntityCert) {
112                     if (decodedCerts.size() == 1) {
113                         builder.addPropertyValue("entityCertificate", decodedCerts.iterator().next());
114                     } else {
115                         throw new FatalBeanException(
116                                 "Config element indicated an entityCertificate, but multiple certs where decoded");
117                     }
118                 }
119             } catch (CertificateException e) {
120                 throw new FatalBeanException("Unable to create X509 credential, unable to parse certificates", e);
121             }
122         }
123 
124         builder.addPropertyValue("certificates", certs);
125     }
126 
127     /**
128      * Extracts the certificate bytes from the content of a Certificate configuration element.
129      * 
130      * @param certConfigContent content of a Certificate configuration element
131      * 
132      * @return certificate bytes
133      */
134     protected abstract byte[] getEncodedCertificate(String certConfigContent);
135 
136     /**
137      * Parses the CRLs from the credential configuration.
138      * 
139      * @param configChildren children of the credential element
140      * @param builder credential build
141      */
142     protected void parseCRLs(Map<QName, List<Element>> configChildren, BeanDefinitionBuilder builder) {
143         List<Element> crlElems = configChildren.get(new QName(SecurityNamespaceHandler.NAMESPACE, "CRL"));
144         if (crlElems == null || crlElems.isEmpty()) {
145             return;
146         }
147 
148         log.debug("Parsing x509 credential CRLs");
149         ArrayList<X509CRL> crls = new ArrayList<X509CRL>();
150         byte[] encodedCRL;
151         Collection<X509CRL> decodedCRLs;
152         for (Element crlElem : crlElems) {
153             encodedCRL = getEncodedCRL(DatatypeHelper.safeTrimOrNullString(crlElem.getTextContent()));
154             if (encodedCRL == null) {
155                 continue;
156             }
157 
158             try {
159                 decodedCRLs = X509Util.decodeCRLs(encodedCRL);
160                 crls.addAll(decodedCRLs);
161             } catch (CRLException e) {
162                 throw new FatalBeanException("Unable to create X509 credential, unable to parse CRLs", e);
163             }
164         }
165 
166         builder.addPropertyValue("crls", crls);
167     }
168 
169     /**
170      * Extracts the CRL(s) bytes from the content of a CRL configuration element.
171      * 
172      * @param certCRLContent content of a CRL configuration element
173      * 
174      * @return CRL bytes
175      */
176     protected abstract byte[] getEncodedCRL(String certCRLContent);
177 }