1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.ksb.messaging;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.security.GeneralSecurityException;
23 import java.security.Signature;
24 import java.security.cert.CertificateFactory;
25
26 import org.apache.commons.codec.binary.Base64;
27 import org.apache.commons.httpclient.Header;
28 import org.apache.commons.httpclient.HttpClient;
29 import org.apache.commons.httpclient.methods.PostMethod;
30 import org.apache.commons.lang.StringUtils;
31 import org.kuali.rice.core.resourceloader.GlobalResourceLoader;
32 import org.kuali.rice.ksb.security.HttpClientHeaderDigitalSigner;
33 import org.kuali.rice.ksb.security.SignatureVerifyingInputStream;
34 import org.kuali.rice.ksb.security.admin.service.JavaSecurityManagementService;
35 import org.kuali.rice.ksb.security.service.DigitalSignatureService;
36 import org.kuali.rice.ksb.util.KSBConstants;
37 import org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor;
38 import org.springframework.remoting.httpinvoker.HttpInvokerClientConfiguration;
39
40
41
42
43
44
45
46
47 public class KSBHttpInvokerRequestExecutor extends CommonsHttpInvokerRequestExecutor {
48
49 private Boolean secure = Boolean.TRUE;
50
51 public KSBHttpInvokerRequestExecutor() {
52 super();
53 }
54
55 public KSBHttpInvokerRequestExecutor(Boolean secure) {
56 super();
57 this.secure = secure;
58 }
59
60 public KSBHttpInvokerRequestExecutor(HttpClient httpClient) {
61 super(httpClient);
62 }
63
64
65
66
67
68 @Override
69 protected void setRequestBody(HttpInvokerClientConfiguration config, PostMethod postMethod, ByteArrayOutputStream baos) throws IOException {
70 if (isSecure()) {
71 try {
72 signRequest(postMethod, baos);
73 } catch (Exception e) {
74 throw new RuntimeException("Failed to sign the outgoing message.", e);
75 }
76 }
77 super.setRequestBody(config, postMethod, baos);
78 }
79
80
81
82
83
84 @Override
85 protected InputStream getResponseBody(HttpInvokerClientConfiguration config, PostMethod postMethod) throws IOException {
86 if (isSecure()) {
87
88 Header digitalSignatureHeader = postMethod.getResponseHeader(KSBConstants.DIGITAL_SIGNATURE_HEADER);
89 Header keyStoreAliasHeader = postMethod.getResponseHeader(KSBConstants.KEYSTORE_ALIAS_HEADER);
90 Header certificateHeader = postMethod.getResponseHeader(KSBConstants.KEYSTORE_CERTIFICATE_HEADER);
91 if (digitalSignatureHeader == null || StringUtils.isEmpty(digitalSignatureHeader.getValue())) {
92 throw new RuntimeException("A digital signature header was required on the response but none was found.");
93 }
94 boolean foundValidKeystoreAlias = (keyStoreAliasHeader != null && StringUtils.isNotBlank(keyStoreAliasHeader.getValue()));
95 boolean foundValidCertificate = (certificateHeader != null && StringUtils.isNotBlank(certificateHeader.getValue()));
96 if (!foundValidCertificate && !foundValidKeystoreAlias) {
97 throw new RuntimeException("Either a key store alias header or a certificate header was required on the response but neither were found.");
98 }
99
100 byte[] digitalSignature = Base64.decodeBase64(digitalSignatureHeader.getValue().getBytes("UTF-8"));
101 String errorQualifier = "General Security Error";
102 try {
103 Signature signature = null;
104 if (foundValidCertificate) {
105 errorQualifier = "Error with given certificate";
106
107 byte[] encodedCertificate = Base64.decodeBase64(certificateHeader.getValue().getBytes("UTF-8"));
108 CertificateFactory cf = CertificateFactory.getInstance("X.509");
109 signature = getDigitalSignatureService().getSignatureForVerification(cf.generateCertificate(new ByteArrayInputStream(encodedCertificate)));
110 } else if (foundValidKeystoreAlias) {
111
112 String keystoreAlias = keyStoreAliasHeader.getValue();
113 errorQualifier = "Error with given alias " + keystoreAlias;
114 signature = getDigitalSignatureService().getSignatureForVerification(keystoreAlias);
115 }
116
117
118 return new SignatureVerifyingInputStream(digitalSignature, signature, super.getResponseBody(config, postMethod));
119 } catch (GeneralSecurityException e) {
120 throw new RuntimeException("Problem verifying signature: " + errorQualifier,e);
121 }
122 }
123 return super.getResponseBody(config, postMethod);
124 }
125
126
127
128 @Override
129 protected void validateResponse(HttpInvokerClientConfiguration config, PostMethod postMethod) throws IOException {
130 if (postMethod.getStatusCode() >= 300) {
131 throw new HttpException(postMethod.getStatusCode(), "Did not receive successful HTTP response: status code = " + postMethod.getStatusCode() +
132 ", status message = [" + postMethod.getStatusText() + "]");
133 }
134 }
135
136
137
138
139 protected void signRequest(PostMethod postMethod, ByteArrayOutputStream baos) throws Exception {
140 Signature signature = getDigitalSignatureService().getSignatureForSigning();
141 HttpClientHeaderDigitalSigner signer = new HttpClientHeaderDigitalSigner(signature, postMethod, getJavaSecurityManagementService().getModuleKeyStoreAlias());
142 signer.getSignature().update(baos.toByteArray());
143 signer.sign();
144 }
145
146 protected boolean isSecure() {
147 return getSecure();
148 }
149
150 public Boolean getSecure() {
151 return this.secure;
152 }
153
154 public void setSecure(Boolean secure) {
155 this.secure = secure;
156 }
157
158 protected DigitalSignatureService getDigitalSignatureService() {
159 return (DigitalSignatureService)GlobalResourceLoader.getService(KSBConstants.ServiceNames.DIGITAL_SIGNATURE_SERVICE);
160 }
161
162 protected JavaSecurityManagementService getJavaSecurityManagementService() {
163 return (JavaSecurityManagementService)GlobalResourceLoader.getService(KSBConstants.ServiceNames.JAVA_SECURITY_MANAGEMENT_SERVICE);
164 }
165
166 }