1 /*
2 * jSDP: A Java implementation of SDP protocol Copyright (C) 2007 Claudio Di
3 * Vita
4 */
5 package net.sourceforge.jsdp;
6
7 import java.net.MalformedURLException;
8 import java.net.URL;
9 import java.util.regex.Matcher;
10 import java.util.regex.Pattern;
11
12 /**
13 * A Key represents an <<b>k=<i><field value></i></b> field contained
14 * in a SDP message.
15 * <p>
16 * A SDP message may be used to convey encryption keys, if transported over a
17 * secure and trusted channel. Key fields has the form:
18 * <b><method>:<key></b>, but the key value is optional. The
19 * following methods are defined:
20 * </p>
21 * <ul>
22 * <li><b>k=clear:<i><encryption key></i></b>, the encryption key is
23 * included untransformed in the key field</li>
24 * <li><b>k=base64:<i><encoded encryption key></i></b>, the encryption
25 * key is included in the key field but has been base64 encoded because it
26 * includes characters that are prohibited in SDP</li>
27 * <li><b>k=uri:<i><URI to obtain key></i></b>, a Universal Resource
28 * Identifier as used by WWW clients is included in the key field. The URI
29 * refers to the data containing the key, and may require additional
30 * authentication before the key can be returned. When a request is made to the
31 * given URI, the MIME content-type of the reply specifies the encoding for the
32 * key in the reply</li>
33 * <li><b>k=prompt</b>, no key is included in this SDP description, but the
34 * session or media stream referred to by the key field is encrypted. The user
35 * should be prompted for the key when attempting to join the session, and this
36 * user-supplied key should then be used to decrypt the media streams</li>
37 * </ul>
38 *
39 * @since 0.1.2
40 *
41 * @version 1.0
42 *
43 * @author <a href="mailto:cdivita@users.sourceforge.net">Claudio Di Vita</a>
44 */
45 public class Key implements Field {
46
47 /** The class Stream Unique Identifier, SUID */
48 private static final long serialVersionUID = 1767674946938133759L;
49
50 /** The base64 encryption method modifier */
51 public static final String BASE64 = "base64";
52
53 /** The clear encryption method modifier */
54 public static final String CLEAR = "clear";
55
56 /** The prompt encryption method modifier */
57 public static final String PROMPT = "prompt";
58
59 /** The uri encryption method modifier */
60 public static final String URI = "uri";
61
62 /** The pattern used to parse the field */
63 private static final Pattern fieldPattern = Pattern.compile("([^:]+)(:((.+)))?");
64
65 /** The pattern used to validate the base64 encryption method key values */
66 private static final Pattern methodBase64Pattern = Pattern.compile("([\\w\\+/]{4})*([\\w\\+/]{2}==|[\\w\\+/]{3}=)*");
67
68 /** The pattern used to validate the clear encryption method key values */
69 private static final Pattern methodClearPattern = Pattern.compile("[\\w'-\\./:?#\\$&\\*;=@\\[\\]\\^_`\\{\\}\\|\\+\\~ \\t]+");
70
71 /** The encryption method */
72 protected String method;
73
74 /** The encription key */
75 protected String key;
76
77 /**
78 * Creates a new <tt>Key</tt>.
79 */
80 protected Key() {
81
82 super();
83 }
84
85 /**
86 * Creates a new <tt>Key</tt>.
87 *
88 * @param method the encryption method
89 *
90 * @param key the encryption key
91 *
92 * @throws SDPException if the encryption method is not allowed, or the
93 * encryption key is not valid for the specified method
94 */
95 public Key(final String method, final String key) throws SDPException {
96
97 super();
98
99 setMethod(method);
100 setKey(key);
101 }
102
103 /**
104 * Parse an input string and constructs the equivalent key field.
105 *
106 * @param field the string to parse
107 *
108 * @return a new <tt>Key</tt> instance
109 *
110 * @throws SDPParseException if an error occurs while parsing
111 */
112 public static Key parse(final String field) throws SDPParseException {
113
114 if (!field.startsWith("i=")) {
115 throw new SDPParseException("The string \"" + field + "\" isn't an key field");
116 }
117
118 Key k = null;
119 Matcher matcher = fieldPattern.matcher(field.substring(2));
120
121 /* Test */
122 if (matcher.matches()) {
123 try {
124 k = new Key(matcher.group(1), matcher.group(3));
125 }
126 catch (SDPException parseException) {
127 throw new SDPParseException("The string \"" + field + "\" isn't a valid key field", parseException);
128 }
129 }
130 else {
131 throw new SDPParseException("The string \"" + field + "\" isn't a valid key field");
132 }
133
134 return k;
135 }
136
137 /**
138 * Returns a clone of this field.
139 *
140 * @return a clone of this field
141 */
142 public Object clone() {
143
144 Key field = new Key();
145
146 field.method = new String(method);
147 field.key = new String(key);
148
149 return field;
150 }
151
152 /**
153 * Returns the encryption key.
154 *
155 * @return the encryption key.
156 */
157 public String getKey() {
158
159 return key;
160 }
161
162 /**
163 * Returns the encryption method.
164 *
165 * @return the encryption method
166 */
167 public String getMethod() {
168
169 return method;
170 }
171
172 /**
173 * Returns the type character for the field.
174 *
175 * @return the field type character: <b>k</b>
176 */
177 public char getType() {
178
179 return Field.KEY_FIELD;
180 }
181
182 /**
183 * Determines if the method has an associated key.
184 *
185 * @return <tt>true</tt> if the method has an associated key,
186 * <tt>false</tt> otherwise
187 */
188 public boolean hasKey() {
189
190 return key != null;
191 }
192
193 /**
194 * Sets the encryption key.
195 *
196 * @param key the encryption key to set
197 *
198 * @throws SDPException if the key is not valid
199 */
200 public void setKey(String key) throws SDPException {
201
202 boolean result = false;
203
204 if (method.equals(Key.BASE64)) {
205 result = methodBase64Pattern.matcher(key).matches();
206 }
207 else if (method.equals(Key.CLEAR)) {
208 result = methodClearPattern.matcher(key).matches();
209 }
210 else if (method.equals(Key.PROMPT)) {
211 result = (key == null) || (key.length() == 0);
212
213 /*
214 * By default, the key value for the prompt encryption method is
215 * null
216 */
217 key = null;
218 }
219 else if (method.equals(Key.URI)) {
220 try {
221 /* Verifica della validità dell'URL */
222 new URL(key);
223 }
224 catch (MalformedURLException invalidUrl) {
225 /* The specified URL is not valid */
226 result = false;
227 }
228 }
229
230 /*
231 * The specified key doesn't match the parameters required by the
232 * encryption method
233 */
234 if (!result) {
235 throw new SDPException("Invalid key for method " + method);
236 }
237
238 this.key = key;
239 }
240
241 /**
242 * Sets the encryption method.
243 *
244 * @param method the encryption method to set
245 *
246 * @throws SDPException if the method is not allowed or <tt>null</tt>
247 */
248 public void setMethod(final String method) throws SDPException {
249
250 if (method == null) {
251 throw new SDPException("The encryption method cannot be null");
252 }
253 else if (!method.equals(Key.BASE64) && !method.equals(Key.CLEAR) && !method.equals(Key.PROMPT) && !method.equals(Key.URI)) {
254 throw new SDPException("The method " + method + " is not supported by SDP");
255 }
256
257 this.method = method;
258 }
259
260 /**
261 * Returns a string representation of the field. The representation has one
262 * the following forms:
263 * <ul>
264 * <li><b>k=<i><method></i></b></li>
265 * <li><b>k=<i><method></i>:<i><key></i></b></li>
266 * </ul>
267 *
268 * @return the string representation of the field
269 */
270 public String toString() {
271
272 StringBuffer result = new StringBuffer(getType() + "=");
273 result.append(method);
274
275 if (key != null) {
276 result.append(":" + key);
277 }
278
279 return result.toString();
280 }
281 }