View Javadoc
1   /*
2    * Copyright 2008-2009 Web Cohesion
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.security.oauth.common.signature;
18  
19  import org.springframework.security.authentication.encoding.PasswordEncoder;
20  import org.springframework.security.core.Authentication;
21  import org.springframework.security.core.context.SecurityContextHolder;
22  import static org.springframework.security.oauth.common.OAuthCodec.oauthEncode;
23  
24  import javax.crypto.spec.SecretKeySpec;
25  import java.io.UnsupportedEncodingException;
26  import java.security.PrivateKey;
27  import java.security.PublicKey;
28  import java.security.cert.X509Certificate;
29  
30  /**
31   * Implements the signatures defined in OAuth Core 1.0. By default, PLAINTEXT signatures are not supported
32   *
33   * @author Ryan Heaton
34   */
35  public class CoreOAuthSignatureMethodFactory implements OAuthSignatureMethodFactory {
36  
37    private boolean supportPlainText = false;
38    private boolean supportHMAC_SHA1 = true;
39    private boolean supportRSA_SHA1 = true;
40    private PasswordEncoder plainTextPasswordEncoder;
41  
42    public OAuthSignatureMethod getSignatureMethod(String methodName, SignatureSecret signatureSecret, String tokenSecret) throws UnsupportedSignatureMethodException {
43      if (supportPlainText && PlainTextSignatureMethod.SIGNATURE_NAME.equals(methodName)) {
44        if (!(signatureSecret instanceof SharedConsumerSecret)) {
45          throw new IllegalArgumentException("Invalid secret for signature method " + methodName + ". Expected a " +
46            SharedConsumerSecret.class.getName() + ", got " + (signatureSecret == null ? "null" : signatureSecret.getClass().getName()) + ".");
47        }
48  
49        String consumerSecret = ((SharedConsumerSecret) signatureSecret).getConsumerSecret();
50        if (consumerSecret == null) {
51          consumerSecret = "";
52        }
53        if (tokenSecret == null) {
54          tokenSecret = "";
55        }
56  
57        consumerSecret = oauthEncode(consumerSecret);
58        tokenSecret = oauthEncode(tokenSecret);
59  
60        Object salt = null;
61        if (signatureSecret instanceof SaltedConsumerSecret) {
62          salt = ((SaltedConsumerSecret) signatureSecret).getSalt();
63        }
64  
65        return new PlainTextSignatureMethod(oauthEncode(new StringBuilder(consumerSecret).append('&').append(tokenSecret).toString()), this.plainTextPasswordEncoder, salt);
66      }
67      else if (supportHMAC_SHA1 && HMAC_SHA1SignatureMethod.SIGNATURE_NAME.equals(methodName)) {
68        if (!(signatureSecret instanceof SharedConsumerSecret)) {
69          throw new IllegalArgumentException("Invalid secret for signature method " + methodName + ". Expected a " +
70            SharedConsumerSecret.class.getName() + ", got " + (signatureSecret == null ? "null" : signatureSecret.getClass().getName()) + ".");
71        }
72  
73        String consumerSecret = ((SharedConsumerSecret) signatureSecret).getConsumerSecret();
74  
75        if (consumerSecret == null) {
76          consumerSecret = "";
77        }
78        if (tokenSecret == null) {
79          tokenSecret = "";
80        }
81  
82        consumerSecret = oauthEncode(consumerSecret);
83        tokenSecret = oauthEncode(tokenSecret);
84  
85        byte[] keyBytes;
86        try {
87          keyBytes = new StringBuilder(consumerSecret).append('&').append(tokenSecret).toString().getBytes("UTF-8");
88        }
89        catch (UnsupportedEncodingException e) {
90          throw new RuntimeException(e.getMessage());
91        }
92        SecretKeySpec spec = new SecretKeySpec(keyBytes, HMAC_SHA1SignatureMethod.MAC_NAME);
93        return new HMAC_SHA1SignatureMethod(spec);
94      }
95      else if (supportRSA_SHA1 && RSA_SHA1SignatureMethod.SIGNATURE_NAME.equals(methodName)) {
96        if (signatureSecret instanceof RSAKeySecret) {
97          PublicKey publicKey = ((RSAKeySecret) signatureSecret).getPublicKey();
98          PrivateKey privateKey = ((RSAKeySecret) signatureSecret).getPrivateKey();
99          return new RSA_SHA1SignatureMethod(privateKey, publicKey);
100       }
101       else {
102         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
103         if (authentication.getCredentials() instanceof X509Certificate) {
104           X509Certificate certificate = (X509Certificate) authentication.getCredentials();
105           if (certificate != null) {
106             return new RSA_SHA1SignatureMethod(certificate.getPublicKey());
107           }
108         }
109       }
110     }
111 
112     throw new UnsupportedSignatureMethodException("Unsupported signature method: " + methodName);    
113   }
114 
115   /**
116    * Whether to support the plain text signature method.
117    *
118    * @return Whether to support the plain text signature method.
119    */
120   public boolean isSupportPlainText() {
121     return supportPlainText;
122   }
123 
124   /**
125    * Whether to support the plain text signature method.
126    *
127    * @param supportPlainText Whether to support the plain text signature method.
128    */
129   public void setSupportPlainText(boolean supportPlainText) {
130     this.supportPlainText = supportPlainText;
131   }
132 
133   /**
134    * Whether to support HMAC-SHA1 signature method.
135    *
136    * @return Whether to support HMAC-SHA1 signature method.
137    */
138   public boolean isSupportHMAC_SHA1() {
139     return supportHMAC_SHA1;
140   }
141 
142   /**
143    * Whether to support HMAC-SHA1 signature method.
144    *
145    * @param supportHMAC_SHA1 Whether to support HMAC-SHA1 signature method.
146    */
147   public void setSupportHMAC_SHA1(boolean supportHMAC_SHA1) {
148     this.supportHMAC_SHA1 = supportHMAC_SHA1;
149   }
150 
151   /**
152    * Whether to support RSA-SHA1 signature method.
153    *
154    * @return Whether to support RSA-SHA1 signature method.
155    */
156   public boolean isSupportRSA_SHA1() {
157     return supportRSA_SHA1;
158   }
159 
160   /**
161    * Whether to support RSA-SHA1 signature method.
162    *
163    * @param supportRSA_SHA1 Whether to support RSA-SHA1 signature method.
164    */
165   public void setSupportRSA_SHA1(boolean supportRSA_SHA1) {
166     this.supportRSA_SHA1 = supportRSA_SHA1;
167   }
168 
169   /**
170    * The password encoder to use for the plain-text password signature method.
171    *
172    * @return The password encoder to use for the plain-text password signature method.
173    */
174   public PasswordEncoder getPlainTextPasswordEncoder() {
175     return plainTextPasswordEncoder;
176   }
177 
178   /**
179    * The password encoder to use for the plain-text password signature method.
180    *
181    * @param plainTextPasswordEncoder The password encoder to use for the plain-text password signature method.
182    */
183   public void setPlainTextPasswordEncoder(PasswordEncoder plainTextPasswordEncoder) {
184     this.plainTextPasswordEncoder = plainTextPasswordEncoder;
185   }
186 }