/*
* Copyright 2008-2009 Web Cohesion
*
* 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.springframework.security.oauth.common.signature;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.springframework.security.oauth.common.OAuthCodec.oauthEncode;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
/**
* Implements the signatures defined in OAuth Core 1.0. By default, PLAINTEXT signatures are not supported
*
* @author Ryan Heaton
*/
public class CoreOAuthSignatureMethodFactory implements OAuthSignatureMethodFactory {
private boolean supportPlainText = false;
private boolean supportHMAC_SHA1 = true;
private boolean supportRSA_SHA1 = true;
private PasswordEncoder plainTextPasswordEncoder;
public OAuthSignatureMethod getSignatureMethod(String methodName, SignatureSecret signatureSecret, String tokenSecret) throws UnsupportedSignatureMethodException {
if (supportPlainText && PlainTextSignatureMethod.SIGNATURE_NAME.equals(methodName)) {
if (!(signatureSecret instanceof SharedConsumerSecret)) {
throw new IllegalArgumentException("Invalid secret for signature method " + methodName + ". Expected a " +
SharedConsumerSecret.class.getName() + ", got " + (signatureSecret == null ? "null" : signatureSecret.getClass().getName()) + ".");
}
String consumerSecret = ((SharedConsumerSecret) signatureSecret).getConsumerSecret();
if (consumerSecret == null) {
consumerSecret = "";
}
if (tokenSecret == null) {
tokenSecret = "";
}
consumerSecret = oauthEncode(consumerSecret);
tokenSecret = oauthEncode(tokenSecret);
Object salt = null;
if (signatureSecret instanceof SaltedConsumerSecret) {
salt = ((SaltedConsumerSecret) signatureSecret).getSalt();
}
return new PlainTextSignatureMethod(oauthEncode(new StringBuilder(consumerSecret).append('&').append(tokenSecret).toString()), this.plainTextPasswordEncoder, salt);
}
else if (supportHMAC_SHA1 && HMAC_SHA1SignatureMethod.SIGNATURE_NAME.equals(methodName)) {
if (!(signatureSecret instanceof SharedConsumerSecret)) {
throw new IllegalArgumentException("Invalid secret for signature method " + methodName + ". Expected a " +
SharedConsumerSecret.class.getName() + ", got " + (signatureSecret == null ? "null" : signatureSecret.getClass().getName()) + ".");
}
String consumerSecret = ((SharedConsumerSecret) signatureSecret).getConsumerSecret();
if (consumerSecret == null) {
consumerSecret = "";
}
if (tokenSecret == null) {
tokenSecret = "";
}
consumerSecret = oauthEncode(consumerSecret);
tokenSecret = oauthEncode(tokenSecret);
byte[] keyBytes;
try {
keyBytes = new StringBuilder(consumerSecret).append('&').append(tokenSecret).toString().getBytes("UTF-8");
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage());
}
SecretKeySpec spec = new SecretKeySpec(keyBytes, HMAC_SHA1SignatureMethod.MAC_NAME);
return new HMAC_SHA1SignatureMethod(spec);
}
else if (supportRSA_SHA1 && RSA_SHA1SignatureMethod.SIGNATURE_NAME.equals(methodName)) {
if (signatureSecret instanceof RSAKeySecret) {
PublicKey publicKey = ((RSAKeySecret) signatureSecret).getPublicKey();
PrivateKey privateKey = ((RSAKeySecret) signatureSecret).getPrivateKey();
return new RSA_SHA1SignatureMethod(privateKey, publicKey);
}
else {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getCredentials() instanceof X509Certificate) {
X509Certificate certificate = (X509Certificate) authentication.getCredentials();
if (certificate != null) {
return new RSA_SHA1SignatureMethod(certificate.getPublicKey());
}
}
}
}
throw new UnsupportedSignatureMethodException("Unsupported signature method: " + methodName);
}
/**
* Whether to support the plain text signature method.
*
* @return Whether to support the plain text signature method.
*/
public boolean isSupportPlainText() {
return supportPlainText;
}
/**
* Whether to support the plain text signature method.
*
* @param supportPlainText Whether to support the plain text signature method.
*/
public void setSupportPlainText(boolean supportPlainText) {
this.supportPlainText = supportPlainText;
}
/**
* Whether to support HMAC-SHA1 signature method.
*
* @return Whether to support HMAC-SHA1 signature method.
*/
public boolean isSupportHMAC_SHA1() {
return supportHMAC_SHA1;
}
/**
* Whether to support HMAC-SHA1 signature method.
*
* @param supportHMAC_SHA1 Whether to support HMAC-SHA1 signature method.
*/
public void setSupportHMAC_SHA1(boolean supportHMAC_SHA1) {
this.supportHMAC_SHA1 = supportHMAC_SHA1;
}
/**
* Whether to support RSA-SHA1 signature method.
*
* @return Whether to support RSA-SHA1 signature method.
*/
public boolean isSupportRSA_SHA1() {
return supportRSA_SHA1;
}
/**
* Whether to support RSA-SHA1 signature method.
*
* @param supportRSA_SHA1 Whether to support RSA-SHA1 signature method.
*/
public void setSupportRSA_SHA1(boolean supportRSA_SHA1) {
this.supportRSA_SHA1 = supportRSA_SHA1;
}
/**
* The password encoder to use for the plain-text password signature method.
*
* @return The password encoder to use for the plain-text password signature method.
*/
public PasswordEncoder getPlainTextPasswordEncoder() {
return plainTextPasswordEncoder;
}
/**
* The password encoder to use for the plain-text password signature method.
*
* @param plainTextPasswordEncoder The password encoder to use for the plain-text password signature method.
*/
public void setPlainTextPasswordEncoder(PasswordEncoder plainTextPasswordEncoder) {
this.plainTextPasswordEncoder = plainTextPasswordEncoder;
}
}