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