/
smime.xml
executable file
·252 lines (210 loc) · 10.6 KB
/
smime.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<chapter>
<title>Body Encryption and Signing via SMIME</title>
<para>S/MIME (Secure/Multipurpose Internet Mail Extensions) is a standard
for public key encryption and signing of MIME data. MIME data being a set of
headers and a message body. Its most often seen in the email world when
somebody wants to encrypt and/or sign an email message they are sending
across the internet. It can also be used for HTTP requests as well which is
what the RESTEasy integration with S/MIME is all about. RESTEasy allows you
to easily encrypt and/or sign an email message using the S/MIME standard.
While the API is described here, you may also want to check out the example
projects that come with the RESTEasy distribution. It shows both Java and
Python clients exchanging S/MIME formatted messages with a JAX-RS
service.</para>
<section>
<title>Maven settings</title>
<para>You must include the resteasy-crypto project to use the smime framework.</para>
<para><programlisting> <dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-crypto</artifactId>
<version>3.6.0-SNAPSHOT</version>
</dependency>
</programlisting></para>
</section>
<section>
<title>Message Body Encryption</title>
<para>While HTTPS is used to encrypt the entire HTTP message, S/MIME
encryption is used solely for the message body of the HTTP request or
response. This is very useful if you have a representation that may be
forwarded by multiple parties (for example, HornetQ's REST Messaging
integration!) and you want to protect the message from prying eyes as it
travels across the network. RESTEasy has two different interfaces for
encrypting message bodies. One for output, one for input. If your client
or server wants to send an HTTP request or response with an encrypted
body, it uses the
<literal>org.jboss.resteasy.security.smime.EnvelopedOutput</literal> type.
Encrypting a body also requires an X509 certificate which can be generated
by the Java keytool command-line interface, or the openssl tool that comes
installed on many OS's. Here's an example of using the
<literal>EnvelopedOutput</literal> interface:</para>
<programlisting>// server side
@Path("encrypted")
@GET
public EnvelopedOutput getEncrypted()
{
Customer cust = new Customer();
cust.setName("Bill");
X509Certificate certificate = ...;
EnvelopedOutput output = new EnvelopedOutput(cust, MediaType.APPLICATION_XML_TYPE);
output.setCertificate(certificate);
return output;
}
// client side
X509Certificate cert = ...;
Customer cust = new Customer();
cust.setName("Bill");
EnvelopedOutput output = new EnvelopedOutput(cust, "application/xml");
output.setCertificate(cert);
Response res = target.request().post(Entity.entity(output, "application/pkcs7-mime").post();
</programlisting>
<para>An EnvelopedOutput instance is created passing in the entity you
want to marshal and the media type you want to marshal it into. So in this
example, we're taking a Customer class and marshalling it into XML before
we encrypt it. RESTEasy will then encrypt the EnvelopedOutput using the
BouncyCastle framework's SMIME integration. The output is a Base64
encoding and would look something like this:</para>
<programlisting>Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7m"
MIAGCSqGSIb3DQEHA6CAMIACAQAxgewwgekCAQAwUjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMK
U29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkAgkA7oW81OriflAw
DQYJKoZIhvcNAQEBBQAEgYCfnqPK/O34DFl2p2zm+xZQ6R+94BqZHdtEWQN2evrcgtAng+f2ltIL
xr/PiK+8bE8wDO5GuCg+k92uYp2rLKlZ5BxCGb8tRM4kYC9sHbH2dPaqzUBhMxjgWdMCX6Q7E130
u9MdGcP74Ogwj8fNl3lD4sx/0k02/QwgaukeY7uNHzCABgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE
CDRozFLsPnSgoIAEQHmqjSKAWlQbuGQL9w4nKw4l+44WgTjKf7mGWZvYY8tOCcdmhDxRSM1Ly682
Imt+LTZf0LXzuFGTsCGOUo742N8AAAAAAAAAAAAA
</programlisting>
<para>Decrypting an S/MIME encrypted message requires using the
org.jboss.resteasy.security.smime.EnvelopedInput interface. You also need
both the private key and X509Certificate used to encrypt the message.
Here's an example:</para>
<programlisting>// server side
@Path("encrypted")
@POST
public void postEncrypted(EnvelopedInput<Customer> input)
{
PrivateKey privateKey = ...;
X509Certificate certificate = ...;
Customer cust = input.getEntity(privateKey, certificate);
}
// client side
ClientRequest request = new ClientRequest("http://localhost:9095/smime/encrypted");
EnvelopedInput input = request.getTarget(EnvelopedInput.class);
Customer cust = (Customer)input.getEntity(Customer.class, privateKey, cert);
</programlisting>
<para>Both examples simply call the getEntity() method passing in the
PrivateKey and X509Certificate instances requires to decrypt the message.
On the server side, a generic is used with EnvelopedInput to specify the
type to marshal to. On the server side this information is passed as a
parameter to getEntity(). The message is in MIME format: a Content-Type
header and body, so the EnvelopedInput class now has everything it needs
to know to both decrypt and unmarshall the entity. </para>
</section>
<section>
<title>Message Body Signing</title>
<para>S/MIME also allows you to digitally sign a message. It is a bit
different than the Doseta Digital Signing Framework. Doseta is an HTTP
header that contains the signature. S/MIME uses the multipart/signed data
format which is a multipart message that contains the entity and the
digital signature. So Doseta is a header, S/MIME is its own media type.
Generally I would prefer Doseta as S/MIME signatures require the client to
know how to parse a multipart message and Doseta doesn't. Its up to you
what you want to use.</para>
<para>RESTEasy has two different interfaces for creating a
multipart/signed message. One for input, one for output. If your client or
server wants to send an HTTP request or response with an multipart/signed
body, it uses the
<literal>org.jboss.resteasy.security.smime.SignedOutput</literal> type.
This type requires both the PrivateKey and X509Certificate to create the
signature. Here's an example of signing an entity and sending a
multipart/signed entity.</para>
<programlisting>// server-side
@Path("signed")
@GET
@Produces("multipart/signed")
public SignedOutput getSigned()
{
Customer cust = new Customer();
cust.setName("Bill");
SignedOutput output = new SignedOutput(cust, MediaType.APPLICATION_XML_TYPE);
output.setPrivateKey(privateKey);
output.setCertificate(certificate);
return output;
}
// client side
Client client = new ResteasyClient();
WebTarget target = client.target("http://localhost:9095/smime/signed");
Customer cust = new Customer();
cust.setName("Bill");
SignedOutput output = new SignedOutput(cust, "application/xml");
output.setPrivateKey(privateKey);
output.setCertificate(cert);
Response res = target.request().post(Entity.entity(output, "multipart/signed");
</programlisting>
<para>An SignedOutput instance is created passing in the entity you want
to marshal and the media type you want to marshal it into. So in this
example, we're taking a Customer class and marshalling it into XML before
we sign it. RESTEasy will then sign the SignedOutput using the
BouncyCastle framework's SMIME integration. The output iwould look
something like this:</para>
<programlisting>Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="----=_Part_0_1083228271.1313024422098"
------=_Part_0_1083228271.1313024422098
Content-Type: application/xml
Content-Transfer-Encoding: 7bit
<customer name="bill"/>
------=_Part_0_1083228271.1313024422098
Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Description: S/MIME Cryptographic Signature
MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAMYIBVzCCAVMC
AQEwUjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJu
ZXQgV2lkZ2l0cyBQdHkgTHRkAgkA7oW81OriflAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL
BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTExMDgxMTAxMDAyMlowIwYJKoZIhvcNAQkEMRYE
FH32BfR1l1vzDshtQvJrgvpGvjADMA0GCSqGSIb3DQEBAQUABIGAL3KVi3ul9cPRUMYcGgQmWtsZ
0bLbAldO+okrt8mQ87SrUv2LGkIJbEhGHsOlsgSU80/YumP+Q4lYsVanVfoI8GgQH3Iztp+Rce2c
y42f86ZypE7ueynI4HTPNHfr78EpyKGzWuZHW4yMo70LpXhk5RqfM9a/n4TEa9QuTU76atAAAAAA
AAA=
------=_Part_0_1083228271.1313024422098--
</programlisting>
<para>To unmarshal and verify a signed message requires using the
<literal>org.jboss.resteasy.security.smime.SignedInput</literal>
interface. You only need the X509Certificate to verify the message. Here's
an example of unmarshalling and verifying a multipart/signed
entity.</para>
<programlisting>// server side
@Path("signed")
@POST
@Consumes("multipart/signed")
public void postSigned(SignedInput<Customer> input) throws Exception
{
Customer cust = input.getEntity();
if (!input.verify(certificate))
{
throw new WebApplicationException(500);
}
}
// client side
Client client = new ResteasyClient();
WebTarget target = client.target("http://localhost:9095/smime/signed");
SignedInput input = target.request().get(SignedInput.class);
Customer cust = (Customer)input.getEntity(Customer.class)
input.verify(cert);
</programlisting>
</section>
<section>
<title>application/pkcs7-signature</title>
<para>
application/pkcs7-signature is a data format that includes both the data and the signature in one ASN.1
binary encoding.
</para>
<para>
SignedOutput and SignedInput can be used to return application/pkcs7-signature format in binary form. Just
change the @Produces or @Consumes to that media type to send back that format.
</para>
<para>
Also, if your @Produces or @Consumes is text/plain instead, SignedOutput will be base64 encoded and sent
as a string.
</para>
</section>
</chapter>