Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/security/CodeSource.java
41152 views
1
/*
2
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.security;
27
28
29
import java.net.URL;
30
import java.net.SocketPermission;
31
import java.util.ArrayList;
32
import java.util.List;
33
import java.util.Hashtable;
34
import java.io.ByteArrayInputStream;
35
import java.io.IOException;
36
import java.security.cert.*;
37
import java.util.Objects;
38
39
import sun.net.util.URLUtil;
40
import sun.security.util.IOUtils;
41
42
/**
43
*
44
* <p>This class extends the concept of a codebase to
45
* encapsulate not only the location (URL) but also the certificate chains
46
* that were used to verify signed code originating from that location.
47
*
48
* @author Li Gong
49
* @author Roland Schemers
50
* @since 1.2
51
*/
52
53
public class CodeSource implements java.io.Serializable {
54
55
@java.io.Serial
56
private static final long serialVersionUID = 4977541819976013951L;
57
58
/**
59
* The code location.
60
*
61
* @serial
62
*/
63
private final URL location;
64
65
/*
66
* The code signers.
67
*/
68
private transient CodeSigner[] signers = null;
69
70
/*
71
* The code signers. Certificate chains are concatenated.
72
*/
73
private transient java.security.cert.Certificate[] certs = null;
74
75
// cached SocketPermission used for matchLocation
76
private transient SocketPermission sp;
77
78
// for generating cert paths
79
private transient CertificateFactory factory = null;
80
81
/**
82
* A String form of the URL for use as a key in HashMaps/Sets. The String
83
* form should be behave in the same manner as the URL when compared for
84
* equality in a HashMap/Set, except that no nameservice lookup is done
85
* on the hostname (only string comparison), and the fragment is not
86
* considered.
87
*/
88
private transient String locationNoFragString;
89
90
/**
91
* Constructs a CodeSource and associates it with the specified
92
* location and set of certificates.
93
*
94
* @param url the location (URL). It may be {@code null}.
95
* @param certs the certificate(s). It may be {@code null}. The contents
96
* of the array are copied to protect against subsequent modification.
97
*/
98
public CodeSource(URL url, java.security.cert.Certificate[] certs) {
99
this.location = url;
100
if (url != null) {
101
this.locationNoFragString = URLUtil.urlNoFragString(url);
102
}
103
104
// Copy the supplied certs
105
if (certs != null) {
106
this.certs = certs.clone();
107
}
108
}
109
110
/**
111
* Constructs a CodeSource and associates it with the specified
112
* location and set of code signers.
113
*
114
* @param url the location (URL). It may be {@code null}.
115
* @param signers the code signers. It may be {@code null}. The contents
116
* of the array are copied to protect against subsequent modification.
117
*
118
* @since 1.5
119
*/
120
public CodeSource(URL url, CodeSigner[] signers) {
121
this.location = url;
122
if (url != null) {
123
this.locationNoFragString = URLUtil.urlNoFragString(url);
124
}
125
126
// Copy the supplied signers
127
if (signers != null) {
128
this.signers = signers.clone();
129
}
130
}
131
132
/**
133
* Returns the hash code value for this object.
134
*
135
* @return a hash code value for this object.
136
*/
137
@Override
138
public int hashCode() {
139
if (location != null)
140
return location.hashCode();
141
else
142
return 0;
143
}
144
145
/**
146
* Tests for equality between the specified object and this
147
* object. Two CodeSource objects are considered equal if their
148
* locations are of identical value and if their signer certificate
149
* chains are of identical value. It is not required that
150
* the certificate chains be in the same order.
151
*
152
* @param obj the object to test for equality with this object.
153
*
154
* @return true if the objects are considered equal, false otherwise.
155
*/
156
@Override
157
public boolean equals(Object obj) {
158
if (obj == this)
159
return true;
160
161
// objects types must be equal
162
return (obj instanceof CodeSource other)
163
&& Objects.equals(location, other.location)
164
&& matchCerts(other, true);
165
}
166
167
/**
168
* Returns the location associated with this CodeSource.
169
*
170
* @return the location (URL), or {@code null} if no URL was supplied
171
* during construction.
172
*/
173
public final URL getLocation() {
174
/* since URL is practically immutable, returning itself is not
175
a security problem */
176
return this.location;
177
}
178
179
/**
180
* Returns a String form of the URL for use as a key in HashMaps/Sets.
181
*/
182
String getLocationNoFragString() {
183
return locationNoFragString;
184
}
185
186
/**
187
* Returns the certificates associated with this CodeSource.
188
* <p>
189
* If this CodeSource object was created using the
190
* {@link #CodeSource(URL url, CodeSigner[] signers)}
191
* constructor then its certificate chains are extracted and used to
192
* create an array of Certificate objects. Each signer certificate is
193
* followed by its supporting certificate chain (which may be empty).
194
* Each signer certificate and its supporting certificate chain is ordered
195
* bottom-to-top (i.e., with the signer certificate first and the (root)
196
* certificate authority last).
197
*
198
* @return a copy of the certificate array, or {@code null} if there
199
* is none.
200
*/
201
public final java.security.cert.Certificate[] getCertificates() {
202
if (certs != null) {
203
return certs.clone();
204
205
} else if (signers != null) {
206
// Convert the code signers to certs
207
ArrayList<java.security.cert.Certificate> certChains =
208
new ArrayList<>();
209
for (int i = 0; i < signers.length; i++) {
210
certChains.addAll(
211
signers[i].getSignerCertPath().getCertificates());
212
}
213
certs = certChains.toArray(
214
new java.security.cert.Certificate[certChains.size()]);
215
return certs.clone();
216
217
} else {
218
return null;
219
}
220
}
221
222
/**
223
* Returns the code signers associated with this CodeSource.
224
* <p>
225
* If this CodeSource object was created using the
226
* {@link #CodeSource(URL url, java.security.cert.Certificate[] certs)}
227
* constructor then its certificate chains are extracted and used to
228
* create an array of CodeSigner objects. Note that only X.509 certificates
229
* are examined - all other certificate types are ignored.
230
*
231
* @return a copy of the code signer array, or {@code null} if there
232
* is none.
233
*
234
* @since 1.5
235
*/
236
public final CodeSigner[] getCodeSigners() {
237
if (signers != null) {
238
return signers.clone();
239
240
} else if (certs != null) {
241
// Convert the certs to code signers
242
signers = convertCertArrayToSignerArray(certs);
243
return signers.clone();
244
245
} else {
246
return null;
247
}
248
}
249
250
/**
251
* Returns true if this CodeSource object "implies" the specified CodeSource.
252
* <p>
253
* More specifically, this method makes the following checks.
254
* If any fail, it returns false. If they all succeed, it returns true.
255
* <ul>
256
* <li> <i>codesource</i> must not be null.
257
* <li> If this object's certificates are not null, then all
258
* of this object's certificates must be present in <i>codesource</i>'s
259
* certificates.
260
* <li> If this object's location (getLocation()) is not null, then the
261
* following checks are made against this object's location and
262
* <i>codesource</i>'s:
263
* <ul>
264
* <li> <i>codesource</i>'s location must not be null.
265
*
266
* <li> If this object's location
267
* equals <i>codesource</i>'s location, then return true.
268
*
269
* <li> This object's protocol (getLocation().getProtocol()) must be
270
* equal to <i>codesource</i>'s protocol, ignoring case.
271
*
272
* <li> If this object's host (getLocation().getHost()) is not null,
273
* then the SocketPermission
274
* constructed with this object's host must imply the
275
* SocketPermission constructed with <i>codesource</i>'s host.
276
*
277
* <li> If this object's port (getLocation().getPort()) is not
278
* equal to -1 (that is, if a port is specified), it must equal
279
* <i>codesource</i>'s port or default port
280
* (codesource.getLocation().getDefaultPort()).
281
*
282
* <li> If this object's file (getLocation().getFile()) doesn't equal
283
* <i>codesource</i>'s file, then the following checks are made:
284
* If this object's file ends with "/-",
285
* then <i>codesource</i>'s file must start with this object's
286
* file (exclusive the trailing "-").
287
* If this object's file ends with a "/*",
288
* then <i>codesource</i>'s file must start with this object's
289
* file and must not have any further "/" separators.
290
* If this object's file doesn't end with a "/",
291
* then <i>codesource</i>'s file must match this object's
292
* file with a '/' appended.
293
*
294
* <li> If this object's reference (getLocation().getRef()) is
295
* not null, it must equal <i>codesource</i>'s reference.
296
*
297
* </ul>
298
* </ul>
299
* <p>
300
* For example, the codesource objects with the following locations
301
* and null certificates all imply
302
* the codesource with the location "http://www.example.com/classes/foo.jar"
303
* and null certificates:
304
* <pre>
305
* http:
306
* http://*.example.com/classes/*
307
* http://www.example.com/classes/-
308
* http://www.example.com/classes/foo.jar
309
* </pre>
310
*
311
* Note that if this CodeSource has a null location and a null
312
* certificate chain, then it implies every other CodeSource.
313
*
314
* @param codesource CodeSource to compare against.
315
*
316
* @return true if the specified codesource is implied by this codesource,
317
* false if not.
318
*/
319
public boolean implies(CodeSource codesource)
320
{
321
if (codesource == null)
322
return false;
323
324
return matchCerts(codesource, false) && matchLocation(codesource);
325
}
326
327
/**
328
* Returns true if all the certs in this
329
* CodeSource are also in <i>that</i>.
330
*
331
* @param that the CodeSource to check against.
332
* @param strict if true then a strict equality match is performed.
333
* Otherwise a subset match is performed.
334
*/
335
boolean matchCerts(CodeSource that, boolean strict)
336
{
337
boolean match;
338
339
// match any key
340
if (certs == null && signers == null) {
341
if (strict) {
342
return (that.certs == null && that.signers == null);
343
} else {
344
return true;
345
}
346
// both have signers
347
} else if (signers != null && that.signers != null) {
348
if (strict && signers.length != that.signers.length) {
349
return false;
350
}
351
for (int i = 0; i < signers.length; i++) {
352
match = false;
353
for (int j = 0; j < that.signers.length; j++) {
354
if (signers[i].equals(that.signers[j])) {
355
match = true;
356
break;
357
}
358
}
359
if (!match) return false;
360
}
361
return true;
362
363
// both have certs
364
} else if (certs != null && that.certs != null) {
365
if (strict && certs.length != that.certs.length) {
366
return false;
367
}
368
for (int i = 0; i < certs.length; i++) {
369
match = false;
370
for (int j = 0; j < that.certs.length; j++) {
371
if (certs[i].equals(that.certs[j])) {
372
match = true;
373
break;
374
}
375
}
376
if (!match) return false;
377
}
378
return true;
379
}
380
381
return false;
382
}
383
384
385
/**
386
* Returns true if two CodeSource's have the "same" location.
387
*
388
* @param that CodeSource to compare against
389
*/
390
private boolean matchLocation(CodeSource that) {
391
if (location == null)
392
return true;
393
394
if ((that == null) || (that.location == null))
395
return false;
396
397
if (location.equals(that.location))
398
return true;
399
400
if (!location.getProtocol().equalsIgnoreCase(that.location.getProtocol()))
401
return false;
402
403
int thisPort = location.getPort();
404
if (thisPort != -1) {
405
int thatPort = that.location.getPort();
406
int port = thatPort != -1 ? thatPort
407
: that.location.getDefaultPort();
408
if (thisPort != port)
409
return false;
410
}
411
412
if (location.getFile().endsWith("/-")) {
413
// Matches the directory and (recursively) all files
414
// and subdirectories contained in that directory.
415
// For example, "/a/b/-" implies anything that starts with
416
// "/a/b/"
417
String thisPath = location.getFile().substring(0,
418
location.getFile().length()-1);
419
if (!that.location.getFile().startsWith(thisPath))
420
return false;
421
} else if (location.getFile().endsWith("/*")) {
422
// Matches the directory and all the files contained in that
423
// directory.
424
// For example, "/a/b/*" implies anything that starts with
425
// "/a/b/" but has no further slashes
426
int last = that.location.getFile().lastIndexOf('/');
427
if (last == -1)
428
return false;
429
String thisPath = location.getFile().substring(0,
430
location.getFile().length()-1);
431
String thatPath = that.location.getFile().substring(0, last+1);
432
if (!thatPath.equals(thisPath))
433
return false;
434
} else {
435
// Exact matches only.
436
// For example, "/a/b" and "/a/b/" both imply "/a/b/"
437
if ((!that.location.getFile().equals(location.getFile()))
438
&& (!that.location.getFile().equals(location.getFile()+"/"))) {
439
return false;
440
}
441
}
442
443
if (location.getRef() != null
444
&& !location.getRef().equals(that.location.getRef())) {
445
return false;
446
}
447
448
String thisHost = location.getHost();
449
String thatHost = that.location.getHost();
450
if (thisHost != null) {
451
if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
452
("".equals(thatHost) || "localhost".equals(thatHost))) {
453
// ok
454
} else if (!thisHost.equals(thatHost)) {
455
if (thatHost == null) {
456
return false;
457
}
458
if (this.sp == null) {
459
this.sp = new SocketPermission(thisHost, "resolve");
460
}
461
if (that.sp == null) {
462
that.sp = new SocketPermission(thatHost, "resolve");
463
}
464
if (!this.sp.implies(that.sp)) {
465
return false;
466
}
467
}
468
}
469
// everything matches
470
return true;
471
}
472
473
/**
474
* Returns a string describing this CodeSource, telling its
475
* URL and certificates.
476
*
477
* @return information about this CodeSource.
478
*/
479
@Override
480
public String toString() {
481
StringBuilder sb = new StringBuilder();
482
sb.append("(");
483
sb.append(this.location);
484
485
if (this.certs != null && this.certs.length > 0) {
486
for (int i = 0; i < this.certs.length; i++) {
487
sb.append( " " + this.certs[i]);
488
}
489
490
} else if (this.signers != null && this.signers.length > 0) {
491
for (int i = 0; i < this.signers.length; i++) {
492
sb.append( " " + this.signers[i]);
493
}
494
} else {
495
sb.append(" <no signer certificates>");
496
}
497
sb.append(")");
498
return sb.toString();
499
}
500
501
/**
502
* Writes this object out to a stream (i.e., serializes it).
503
*
504
* @serialData An initial {@code URL} is followed by an
505
* {@code int} indicating the number of certificates to follow
506
* (a value of "zero" denotes that there are no certificates associated
507
* with this object).
508
* Each certificate is written out starting with a {@code String}
509
* denoting the certificate type, followed by an
510
* {@code int} specifying the length of the certificate encoding,
511
* followed by the certificate encoding itself which is written out as an
512
* array of bytes. Finally, if any code signers are present then the array
513
* of code signers is serialized and written out too.
514
*
515
* @param oos the {@code ObjectOutputStream} to which data is written
516
* @throws IOException if an I/O error occurs
517
*/
518
@java.io.Serial
519
private void writeObject(java.io.ObjectOutputStream oos)
520
throws IOException
521
{
522
oos.defaultWriteObject(); // location
523
524
// Serialize the array of certs
525
if (certs == null || certs.length == 0) {
526
oos.writeInt(0);
527
} else {
528
// write out the total number of certs
529
oos.writeInt(certs.length);
530
// write out each cert, including its type
531
for (int i = 0; i < certs.length; i++) {
532
java.security.cert.Certificate cert = certs[i];
533
try {
534
oos.writeUTF(cert.getType());
535
byte[] encoded = cert.getEncoded();
536
oos.writeInt(encoded.length);
537
oos.write(encoded);
538
} catch (CertificateEncodingException cee) {
539
throw new IOException(cee.getMessage());
540
}
541
}
542
}
543
544
// Serialize the array of code signers (if any)
545
if (signers != null && signers.length > 0) {
546
oos.writeObject(signers);
547
}
548
}
549
550
/**
551
* Restores this object from a stream (i.e., deserializes it).
552
*
553
* @param ois the {@code ObjectInputStream} from which data is read
554
* @throws IOException if an I/O error occurs
555
* @throws ClassNotFoundException if a serialized class cannot be loaded
556
*/
557
@java.io.Serial
558
private void readObject(java.io.ObjectInputStream ois)
559
throws IOException, ClassNotFoundException
560
{
561
CertificateFactory cf;
562
Hashtable<String, CertificateFactory> cfs = null;
563
List<java.security.cert.Certificate> certList = null;
564
565
ois.defaultReadObject(); // location
566
567
// process any new-style certs in the stream (if present)
568
int size = ois.readInt();
569
if (size > 0) {
570
// we know of 3 different cert types: X.509, PGP, SDSI, which
571
// could all be present in the stream at the same time
572
cfs = new Hashtable<>(3);
573
certList = new ArrayList<>(size > 20 ? 20 : size);
574
} else if (size < 0) {
575
throw new IOException("size cannot be negative");
576
}
577
578
for (int i = 0; i < size; i++) {
579
// read the certificate type, and instantiate a certificate
580
// factory of that type (reuse existing factory if possible)
581
String certType = ois.readUTF();
582
if (cfs.containsKey(certType)) {
583
// reuse certificate factory
584
cf = cfs.get(certType);
585
} else {
586
// create new certificate factory
587
try {
588
cf = CertificateFactory.getInstance(certType);
589
} catch (CertificateException ce) {
590
throw new ClassNotFoundException
591
("Certificate factory for " + certType + " not found");
592
}
593
// store the certificate factory so we can reuse it later
594
cfs.put(certType, cf);
595
}
596
// parse the certificate
597
byte[] encoded = IOUtils.readExactlyNBytes(ois, ois.readInt());
598
ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
599
try {
600
certList.add(cf.generateCertificate(bais));
601
} catch (CertificateException ce) {
602
throw new IOException(ce.getMessage());
603
}
604
bais.close();
605
}
606
607
if (certList != null) {
608
this.certs = certList.toArray(
609
new java.security.cert.Certificate[size]);
610
}
611
// Deserialize array of code signers (if any)
612
try {
613
this.signers = ((CodeSigner[])ois.readObject()).clone();
614
} catch (IOException ioe) {
615
// no signers present
616
}
617
618
if (location != null) {
619
locationNoFragString = URLUtil.urlNoFragString(location);
620
}
621
}
622
623
/*
624
* Convert an array of certificates to an array of code signers.
625
* The array of certificates is a concatenation of certificate chains
626
* where the initial certificate in each chain is the end-entity cert.
627
*
628
* @return an array of code signers or null if none are generated.
629
*/
630
private CodeSigner[] convertCertArrayToSignerArray(
631
java.security.cert.Certificate[] certs) {
632
633
if (certs == null) {
634
return null;
635
}
636
637
try {
638
// Initialize certificate factory
639
if (factory == null) {
640
factory = CertificateFactory.getInstance("X.509");
641
}
642
643
// Iterate through all the certificates
644
int i = 0;
645
List<CodeSigner> signers = new ArrayList<>();
646
while (i < certs.length) {
647
List<java.security.cert.Certificate> certChain =
648
new ArrayList<>();
649
certChain.add(certs[i++]); // first cert is an end-entity cert
650
int j = i;
651
652
// Extract chain of certificates
653
// (loop while certs are not end-entity certs)
654
while (j < certs.length &&
655
certs[j] instanceof X509Certificate &&
656
((X509Certificate)certs[j]).getBasicConstraints() != -1) {
657
certChain.add(certs[j]);
658
j++;
659
}
660
i = j;
661
CertPath certPath = factory.generateCertPath(certChain);
662
signers.add(new CodeSigner(certPath, null));
663
}
664
665
if (signers.isEmpty()) {
666
return null;
667
} else {
668
return signers.toArray(new CodeSigner[signers.size()]);
669
}
670
671
} catch (CertificateException e) {
672
return null; //TODO - may be better to throw an ex. here
673
}
674
}
675
}
676
677