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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
|
/*
* Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.x509;
import java.io.IOException;
import java.lang.Integer;
import java.net.InetAddress;
import java.util.Arrays;
import sun.misc.HexDumpEncoder;
import sun.security.util.BitArray;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
/**
* This class implements the IPAddressName as required by the GeneralNames
* ASN.1 object. Both IPv4 and IPv6 addresses are supported using the
* formats specified in IETF PKIX RFC2459.
* <p>
* [RFC2459 4.2.1.7 Subject Alternative Name]
* When the subjectAltName extension contains a iPAddress, the address
* MUST be stored in the octet string in "network byte order," as
* specified in RFC 791. The least significant bit (LSB) of
* each octet is the LSB of the corresponding byte in the network
* address. For IP Version 4, as specified in RFC 791, the octet string
* MUST contain exactly four octets. For IP Version 6, as specified in
* RFC 1883, the octet string MUST contain exactly sixteen octets.
* <p>
* [RFC2459 4.2.1.11 Name Constraints]
* The syntax of iPAddress MUST be as described in section 4.2.1.7 with
* the following additions specifically for Name Constraints. For IPv4
* addresses, the ipAddress field of generalName MUST contain eight (8)
* octets, encoded in the style of RFC 1519 (CIDR) to represent an
* address range.[RFC 1519] For IPv6 addresses, the ipAddress field
* MUST contain 32 octets similarly encoded. For example, a name
* constraint for "class C" subnet 10.9.8.0 shall be represented as the
* octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
* 10.9.8.0/255.255.255.0.
* <p>
* @see GeneralName
* @see GeneralNameInterface
* @see GeneralNames
*
*
* @author Amit Kapoor
* @author Hemma Prafullchandra
*/
public class IPAddressName implements GeneralNameInterface {
private byte[] address;
private boolean isIPv4;
private String name;
/**
* Create the IPAddressName object from the passed encoded Der value.
*
* @params derValue the encoded DER IPAddressName.
* @exception IOException on error.
*/
public IPAddressName(DerValue derValue) throws IOException {
this(derValue.getOctetString());
}
/**
* Create the IPAddressName object with the specified octets.
*
* @params address the IP address
* @throws IOException if address is not a valid IPv4 or IPv6 address
*/
public IPAddressName(byte[] address) throws IOException {
/*
* A valid address must consist of 4 bytes of address and
* optional 4 bytes of 4 bytes of mask, or 16 bytes of address
* and optional 16 bytes of mask.
*/
if (address.length == 4 || address.length == 8) {
isIPv4 = true;
} else if (address.length == 16 || address.length == 32) {
isIPv4 = false;
} else {
throw new IOException("Invalid IPAddressName");
}
this.address = address;
}
/**
* Create an IPAddressName from a String.
* [IETF RFC1338 Supernetting & IETF RFC1519 Classless Inter-Domain
* Routing (CIDR)] For IPv4 addresses, the forms are
* "b1.b2.b3.b4" or "b1.b2.b3.b4/m1.m2.m3.m4", where b1 - b4 are decimal
* byte values 0-255 and m1 - m4 are decimal mask values
* 0 - 255.
* <p>
* [IETF RFC2373 IP Version 6 Addressing Architecture]
* For IPv6 addresses, the forms are "a1:a2:...:a8" or "a1:a2:...:a8/n",
* where a1-a8 are hexadecimal values representing the eight 16-bit pieces
* of the address. If /n is used, n is a decimal number indicating how many
* of the leftmost contiguous bits of the address comprise the prefix for
* this subnet. Internally, a mask value is created using the prefix length.
* <p>
* @param name String form of IPAddressName
* @throws IOException if name can not be converted to a valid IPv4 or IPv6
* address
*/
public IPAddressName(String name) throws IOException {
if (name == null || name.length() == 0) {
throw new IOException("IPAddress cannot be null or empty");
}
if (name.charAt(name.length() - 1) == '/') {
throw new IOException("Invalid IPAddress: " + name);
}
if (name.indexOf(':') >= 0) {
// name is IPv6: uses colons as value separators
// Parse name into byte-value address components and optional
// prefix
parseIPv6(name);
isIPv4 = false;
} else if (name.indexOf('.') >= 0) {
//name is IPv4: uses dots as value separators
parseIPv4(name);
isIPv4 = true;
} else {
throw new IOException("Invalid IPAddress: " + name);
}
}
/**
* Parse an IPv4 address.
*
* @param name IPv4 address with optional mask values
* @throws IOException on error
*/
private void parseIPv4(String name) throws IOException {
// Parse name into byte-value address components
int slashNdx = name.indexOf('/');
if (slashNdx == -1) {
address = InetAddress.getByName(name).getAddress();
} else {
address = new byte[8];
// parse mask
byte[] mask = InetAddress.getByName
(name.substring(slashNdx+1)).getAddress();
// parse base address
byte[] host = InetAddress.getByName
(name.substring(0, slashNdx)).getAddress();
System.arraycopy(host, 0, address, 0, 4);
System.arraycopy(mask, 0, address, 4, 4);
}
}
/**
* Parse an IPv6 address.
*
* @param name String IPv6 address with optional /<prefix length>
* If /<prefix length> is present, address[] array will
* be 32 bytes long, otherwise 16.
* @throws IOException on error
*/
private final static int MASKSIZE = 16;
private void parseIPv6(String name) throws IOException {
int slashNdx = name.indexOf('/');
if (slashNdx == -1) {
address = InetAddress.getByName(name).getAddress();
} else {
address = new byte[32];
byte[] base = InetAddress.getByName
(name.substring(0, slashNdx)).getAddress();
System.arraycopy(base, 0, address, 0, 16);
// append a mask corresponding to the num of prefix bits specified
int prefixLen = Integer.parseInt(name.substring(slashNdx+1));
if (prefixLen < 0 || prefixLen > 128) {
throw new IOException("IPv6Address prefix length (" +
prefixLen + ") in out of valid range [0,128]");
}
// create new bit array initialized to zeros
BitArray bitArray = new BitArray(MASKSIZE * 8);
// set all most significant bits up to prefix length
for (int i = 0; i < prefixLen; i++)
bitArray.set(i, true);
byte[] maskArray = bitArray.toByteArray();
// copy mask bytes into mask portion of address
for (int i = 0; i < MASKSIZE; i++)
address[MASKSIZE+i] = maskArray[i];
}
}
/**
* Return the type of the GeneralName.
*/
public int getType() {
return NAME_IP;
}
/**
* Encode the IPAddress name into the DerOutputStream.
*
* @params out the DER stream to encode the IPAddressName to.
* @exception IOException on encoding errors.
*/
public void encode(DerOutputStream out) throws IOException {
out.putOctetString(address);
}
/**
* Return a printable string of IPaddress
*/
public String toString() {
try {
return "IPAddress: " + getName();
} catch (IOException ioe) {
// dump out hex rep for debugging purposes
HexDumpEncoder enc = new HexDumpEncoder();
return "IPAddress: " + enc.encodeBuffer(address);
}
}
/**
* Return a standard String representation of IPAddress.
* See IPAddressName(String) for the formats used for IPv4
* and IPv6 addresses.
*
* @throws IOException if the IPAddress cannot be converted to a String
*/
public String getName() throws IOException {
if (name != null)
return name;
if (isIPv4) {
//IPv4 address or subdomain
byte[] host = new byte[4];
System.arraycopy(address, 0, host, 0, 4);
name = InetAddress.getByAddress(host).getHostAddress();
if (address.length == 8) {
byte[] mask = new byte[4];
System.arraycopy(address, 4, mask, 0, 4);
name = name + "/" +
InetAddress.getByAddress(mask).getHostAddress();
}
} else {
//IPv6 address or subdomain
byte[] host = new byte[16];
System.arraycopy(address, 0, host, 0, 16);
name = InetAddress.getByAddress(host).getHostAddress();
if (address.length == 32) {
// IPv6 subdomain: display prefix length
// copy subdomain into new array and convert to BitArray
byte[] maskBytes = new byte[16];
for (int i=16; i < 32; i++)
maskBytes[i-16] = address[i];
BitArray ba = new BitArray(16*8, maskBytes);
// Find first zero bit
int i=0;
for (; i < 16*8; i++) {
if (!ba.get(i))
break;
}
name = name + "/" + i;
// Verify remaining bits 0
for (; i < 16*8; i++) {
if (ba.get(i)) {
throw new IOException("Invalid IPv6 subdomain - set " +
"bit " + i + " not contiguous");
}
}
}
}
return name;
}
/**
* Returns this IPAddress name as a byte array.
*/
public byte[] getBytes() {
return address.clone();
}
/**
* Compares this name with another, for equality.
*
* @return true iff the names are identical.
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof IPAddressName))
return false;
IPAddressName otherName = (IPAddressName)obj;
byte[] other = otherName.address;
if (other.length != address.length)
return false;
if (address.length == 8 || address.length == 32) {
// Two subnet addresses
// Mask each and compare masked values
int maskLen = address.length/2;
for (int i=0; i < maskLen; i++) {
byte maskedThis = (byte)(address[i] & address[i+maskLen]);
byte maskedOther = (byte)(other[i] & other[i+maskLen]);
if (maskedThis != maskedOther) {
return false;
}
}
// Now compare masks
for (int i=maskLen; i < address.length; i++)
if (address[i] != other[i])
return false;
return true;
} else {
// Two IPv4 host addresses or two IPv6 host addresses
// Compare bytes
return Arrays.equals(other, address);
}
}
/**
* Returns the hash code value for this object.
*
* @return a hash code value for this object.
*/
public int hashCode() {
int retval = 0;
for (int i=0; i<address.length; i++)
retval += address[i] * i;
return retval;
}
/**
* Return type of constraint inputName places on this name:<ul>
* <li>NAME_DIFF_TYPE = -1: input name is different type from name
* (i.e. does not constrain).
* <li>NAME_MATCH = 0: input name matches name.
* <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming
* subtree)
* <li>NAME_WIDENS = 2: input name widens name (is higher in the naming
* subtree)
* <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but
* is same type.
* </ul>. These results are used in checking NameConstraints during
* certification path verification.
* <p>
* [RFC2459] The syntax of iPAddress MUST be as described in section
* 4.2.1.7 with the following additions specifically for Name Constraints.
* For IPv4 addresses, the ipAddress field of generalName MUST contain
* eight (8) octets, encoded in the style of RFC 1519 (CIDR) to represent an
* address range.[RFC 1519] For IPv6 addresses, the ipAddress field
* MUST contain 32 octets similarly encoded. For example, a name
* constraint for "class C" subnet 10.9.8.0 shall be represented as the
* octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
* 10.9.8.0/255.255.255.0.
* <p>
* @param inputName to be checked for being constrained
* @returns constraint type above
* @throws UnsupportedOperationException if name is not exact match, but
* narrowing and widening are not supported for this name type.
*/
public int constrains(GeneralNameInterface inputName)
throws UnsupportedOperationException {
int constraintType;
if (inputName == null)
constraintType = NAME_DIFF_TYPE;
else if (inputName.getType() != NAME_IP)
constraintType = NAME_DIFF_TYPE;
else if (((IPAddressName)inputName).equals(this))
constraintType = NAME_MATCH;
else {
IPAddressName otherName = (IPAddressName)inputName;
byte[] otherAddress = otherName.address;
if (otherAddress.length == 4 && address.length == 4)
// Two host addresses
constraintType = NAME_SAME_TYPE;
else if ((otherAddress.length == 8 && address.length == 8) ||
(otherAddress.length == 32 && address.length == 32)) {
// Two subnet addresses
// See if one address fully encloses the other address
boolean otherSubsetOfThis = true;
boolean thisSubsetOfOther = true;
boolean thisEmpty = false;
boolean otherEmpty = false;
int maskOffset = address.length/2;
for (int i=0; i < maskOffset; i++) {
if ((byte)(address[i] & address[i+maskOffset]) != address[i])
thisEmpty=true;
if ((byte)(otherAddress[i] & otherAddress[i+maskOffset]) != otherAddress[i])
otherEmpty=true;
if (!(((byte)(address[i+maskOffset] & otherAddress[i+maskOffset]) == address[i+maskOffset]) &&
((byte)(address[i] & address[i+maskOffset]) == (byte)(otherAddress[i] & address[i+maskOffset])))) {
otherSubsetOfThis = false;
}
if (!(((byte)(otherAddress[i+maskOffset] & address[i+maskOffset]) == otherAddress[i+maskOffset]) &&
((byte)(otherAddress[i] & otherAddress[i+maskOffset]) == (byte)(address[i] & otherAddress[i+maskOffset])))) {
thisSubsetOfOther = false;
}
}
if (thisEmpty || otherEmpty) {
if (thisEmpty && otherEmpty)
constraintType = NAME_MATCH;
else if (thisEmpty)
constraintType = NAME_WIDENS;
else
constraintType = NAME_NARROWS;
} else if (otherSubsetOfThis)
constraintType = NAME_NARROWS;
else if (thisSubsetOfOther)
constraintType = NAME_WIDENS;
else
constraintType = NAME_SAME_TYPE;
} else if (otherAddress.length == 8 || otherAddress.length == 32) {
//Other is a subnet, this is a host address
int i = 0;
int maskOffset = otherAddress.length/2;
for (; i < maskOffset; i++) {
// Mask this address by other address mask and compare to other address
// If all match, then this address is in other address subnet
if ((address[i] & otherAddress[i+maskOffset]) != otherAddress[i])
break;
}
if (i == maskOffset)
constraintType = NAME_WIDENS;
else
constraintType = NAME_SAME_TYPE;
} else if (address.length == 8 || address.length == 32) {
//This is a subnet, other is a host address
int i = 0;
int maskOffset = address.length/2;
for (; i < maskOffset; i++) {
// Mask other address by this address mask and compare to this address
if ((otherAddress[i] & address[i+maskOffset]) != address[i])
break;
}
if (i == maskOffset)
constraintType = NAME_NARROWS;
else
constraintType = NAME_SAME_TYPE;
} else {
constraintType = NAME_SAME_TYPE;
}
}
return constraintType;
}
/**
* Return subtree depth of this name for purposes of determining
* NameConstraints minimum and maximum bounds and for calculating
* path lengths in name subtrees.
*
* @returns distance of name from root
* @throws UnsupportedOperationException if not supported for this name type
*/
public int subtreeDepth() throws UnsupportedOperationException {
throw new UnsupportedOperationException
("subtreeDepth() not defined for IPAddressName");
}
}
|