View Javadoc

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>&lt;field value&gt;</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>&lt;method&gt;:&lt;key&gt;</b>, but the key value is optional. The
19   * following methods are defined:
20   * </p>
21   * <ul>
22   * <li><b>k=clear:<i>&lt;encryption key&gt;</i></b>, the encryption key is
23   * included untransformed in the key field</li>
24   * <li><b>k=base64:<i>&lt;encoded encryption key&gt;</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>&lt;URI to obtain key&gt;</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>&lt;method&gt;</i></b></li>
265      * <li><b>k=<i>&lt;method&gt;</i>:<i>&lt;key&gt;</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 }