1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.crypto.script

File RSACryptoScriptService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart0.png
83% of files have more coverage

Code metrics

32
65
21
1
556
278
40
0.62
3.1
21
1.9

Classes

Class Line # Actions
RSACryptoScriptService 80 65 0% 40 118
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.crypto.script;
21   
22    import java.io.IOException;
23    import java.math.BigInteger;
24    import java.nio.charset.Charset;
25    import java.security.GeneralSecurityException;
26    import java.util.Collection;
27    import java.util.Date;
28    import java.util.EnumSet;
29    import java.util.HashSet;
30    import java.util.List;
31    import java.util.Set;
32   
33    import javax.inject.Inject;
34    import javax.inject.Named;
35    import javax.inject.Provider;
36    import javax.inject.Singleton;
37   
38    import org.xwiki.component.annotation.Component;
39    import org.xwiki.crypto.KeyPairGenerator;
40    import org.xwiki.crypto.params.cipher.asymmetric.AsymmetricKeyPair;
41    import org.xwiki.crypto.params.cipher.asymmetric.PrivateKeyParameters;
42    import org.xwiki.crypto.params.cipher.asymmetric.PublicKeyParameters;
43    import org.xwiki.crypto.params.generator.asymmetric.RSAKeyGenerationParameters;
44    import org.xwiki.crypto.pkix.CertificateChainBuilder;
45    import org.xwiki.crypto.pkix.CertificateGeneratorFactory;
46    import org.xwiki.crypto.pkix.CertificateProvider;
47    import org.xwiki.crypto.pkix.CertifyingSigner;
48    import org.xwiki.crypto.pkix.X509ExtensionBuilder;
49    import org.xwiki.crypto.pkix.params.CertifiedKeyPair;
50    import org.xwiki.crypto.pkix.params.CertifiedPublicKey;
51    import org.xwiki.crypto.pkix.params.x509certificate.DistinguishedName;
52    import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateGenerationParameters;
53    import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateParameters;
54    import org.xwiki.crypto.pkix.params.x509certificate.X509CertifiedPublicKey;
55    import org.xwiki.crypto.pkix.params.x509certificate.extension.ExtendedKeyUsages;
56    import org.xwiki.crypto.pkix.params.x509certificate.extension.KeyUsage;
57    import org.xwiki.crypto.pkix.params.x509certificate.extension.X509DnsName;
58    import org.xwiki.crypto.pkix.params.x509certificate.extension.X509GeneralName;
59    import org.xwiki.crypto.pkix.params.x509certificate.extension.X509IpAddress;
60    import org.xwiki.crypto.pkix.params.x509certificate.extension.X509Rfc822Name;
61    import org.xwiki.crypto.signer.CMSSignedDataGenerator;
62    import org.xwiki.crypto.signer.CMSSignedDataVerifier;
63    import org.xwiki.crypto.signer.SignerFactory;
64    import org.xwiki.crypto.signer.param.CMSSignedDataGeneratorParameters;
65    import org.xwiki.crypto.signer.param.CMSSignedDataVerified;
66    import org.xwiki.crypto.signer.param.CMSSignerInfo;
67    import org.xwiki.script.service.ScriptService;
68    import org.xwiki.stability.Unstable;
69   
70    /**
71    * Script service allowing a user to create keys pairs and issue certificates.
72    *
73    * @version $Id: faf0af2f6e9bd47ec7e14c5f0223b53863194047 $
74    * @since 8.4RC1
75    */
76    @Component
77    @Named(CryptoScriptService.ROLEHINT + '.' + RSACryptoScriptService.ROLEHINT)
78    @Singleton
79    @Unstable
 
80    public class RSACryptoScriptService implements ScriptService
81    {
82    /**
83    * The role hint of this component.
84    */
85    public static final String ROLEHINT = "rsa";
86   
87    private static final Charset UTF8 = Charset.forName("UTF-8");
88   
89    @Inject
90    @Named("RSA")
91    private KeyPairGenerator keyPairGenerator;
92   
93    @Inject
94    @Named("SHA1withRSAEncryption")
95    private SignerFactory signerFactory;
96   
97    @Inject
98    private Provider<X509ExtensionBuilder> extensionBuilder;
99   
100    @Inject
101    @Named("X509")
102    private CertificateGeneratorFactory certificateGeneratorFactory;
103   
104    @Inject
105    private CMSSignedDataGenerator cmsSignedDataGenerator;
106   
107    @Inject
108    @Named("X509")
109    private CertificateChainBuilder certificateChainBuilder;
110   
111    @Inject
112    private CMSSignedDataVerifier cmsSignedDataVerifier;
113   
114    /**
115    * Generate a new RSA key pair.
116    *
117    * The key strength will be {@value RSAKeyGenerationParameters#DEFAULT_STRENGTH}.
118    * The key public exponent will be 0x10001.
119    * The probability a chosen prime could not be a real prime will be smaller
120    * than 2^-{@value RSAKeyGenerationParameters#DEFAULT_CERTAINTY}.
121    *
122    * @return an new asymmetric key pair.
123    */
 
124  0 toggle public AsymmetricKeyPair generateKeyPair()
125    {
126  0 return keyPairGenerator.generate();
127    }
128   
129    /**
130    * Generate a new RSA key pair of given strength. The strength should be given in number of bytes, so for
131    * a 2048 bits key, you should use 256 (bytes) as the integer parameter. The minimum valid strength is 2.
132    *
133    * The key public exponent will be 0x10001.
134    * The probability a chosen prime could not be a real prime will be smaller
135    * than 2^-{@value RSAKeyGenerationParameters#DEFAULT_CERTAINTY}.
136    *
137    * @param strength the strength in bytes.
138    * @return an new asymmetric key pair.
139    */
 
140  0 toggle public AsymmetricKeyPair generateKeyPair(int strength)
141    {
142  0 return keyPairGenerator.generate(new RSAKeyGenerationParameters(strength));
143    }
144   
145    /**
146    * Build a new instance with all custom parameters. The strength
147    * should be given in number of bytes, so for a 2048 bits key, you should use 256 (bytes) as the integer parameter.
148    * The minimum valid strength is 2. The exponent should be an odd number. The probability a chosen prime could
149    * not be a real prime will be smaller than 2^certainty.
150    *
151    * @param strength the key strength in bytes.
152    * @param publicExponent the public exponent.
153    * @param certainty certainty for prime evaluation.
154    *
155    * @return an new asymmetric key pair.
156    */
 
157  0 toggle public AsymmetricKeyPair generateKeyPair(int strength, BigInteger publicExponent, int certainty)
158    {
159  0 return keyPairGenerator.generate(new RSAKeyGenerationParameters(strength, publicExponent, certainty));
160    }
161   
162    /**
163    * Create a CertifiedKeyPair from a private key and a certificate.
164    *
165    * @param privateKey the private key.
166    * @param certificate the certified public key.
167    * @return a certified key pair.
168    */
 
169  0 toggle public CertifiedKeyPair createCertifiedKeyPair(PrivateKeyParameters privateKey, CertifiedPublicKey certificate)
170    {
171  0 return new CertifiedKeyPair(privateKey, certificate);
172    }
173   
174    /**
175    * Create a self-signed certificate for a Root CA.
176    *
177    * @param keyPair the keypair to issue the certificate for and used for signing it.
178    * @param dn the distinguished name for the new the certificate.
179    * @param validity the validity of the certificate from now in days.
180    * @return a certified public key.
181    * @throws IOException in case on error while reading the public key.
182    * @throws GeneralSecurityException in case of error.
183    */
 
184  0 toggle public CertifiedKeyPair issueRootCACertificate(AsymmetricKeyPair keyPair, String dn, int validity)
185    throws IOException, GeneralSecurityException
186    {
187  0 return new CertifiedKeyPair(
188    keyPair.getPrivate(),
189    certificateGeneratorFactory.getInstance(signerFactory.getInstance(true, keyPair.getPrivate()),
190    new X509CertificateGenerationParameters(
191    validity,
192    extensionBuilder.get().addBasicConstraints(true)
193    .addKeyUsage(true, EnumSet.of(KeyUsage.keyCertSign,
194    KeyUsage.cRLSign))
195    .build()))
196    .generate(new DistinguishedName(dn), keyPair.getPublic(),
197    new X509CertificateParameters())
198    );
199    }
200   
201    /**
202    * Create an intermediate CA certificate.
203    *
204    * @param issuer the certified keypair for issuing the certificate
205    * @param keyPair the keyPair of the public key to certify
206    * @param dn the distinguished name for the new the certificate.
207    * @param validity the validity of the certificate from now in days.
208    * @return a certified keypair.
209    * @throws IOException in case on error while reading the public key.
210    * @throws GeneralSecurityException in case of error.
211    */
 
212  0 toggle public CertifiedKeyPair issueIntermediateCertificate(CertifiedKeyPair issuer, AsymmetricKeyPair keyPair,
213    String dn, int validity)
214    throws IOException, GeneralSecurityException
215    {
216  0 return new CertifiedKeyPair(
217    keyPair.getPrivate(),
218    issueIntermediateCertificate(issuer, keyPair.getPublic(), dn, validity)
219    );
220    }
221   
222    /**
223    * Create an intermediate CA certificate.
224    *
225    * @param privateKey the private key for signing the certificate
226    * @param issuer the certificate of the issuer of the certificate
227    * @param publicKey the public key to certify
228    * @param dn the distinguished name for the new the certificate.
229    * @param validity the validity of the certificate from now in days.
230    * @return a certified public key.
231    * @throws IOException in case on error while reading the public key.
232    * @throws GeneralSecurityException in case of error.
233    */
 
234  0 toggle public CertifiedPublicKey issueIntermediateCertificate(PrivateKeyParameters privateKey, CertifiedPublicKey issuer,
235    PublicKeyParameters publicKey, String dn, int validity)
236    throws IOException, GeneralSecurityException
237    {
238  0 return issueIntermediateCertificate(new CertifiedKeyPair(privateKey, issuer), publicKey, dn, validity);
239    }
240   
241    /**
242    * Create an intermediate CA certificate.
243    *
244    * @param issuer the certified keypair for issuing the certificate
245    * @param publicKey the public key to certify
246    * @param dn the distinguished name for the new the certificate.
247    * @param validity the validity of the certificate from now in days.
248    * @return a certified public key.
249    * @throws IOException in case on error while reading the public key.
250    * @throws GeneralSecurityException in case of error.
251    */
 
252  0 toggle public CertifiedPublicKey issueIntermediateCertificate(CertifiedKeyPair issuer, PublicKeyParameters publicKey,
253    String dn, int validity)
254    throws IOException, GeneralSecurityException
255    {
256  0 return certificateGeneratorFactory.getInstance(
257    CertifyingSigner.getInstance(true, issuer, signerFactory),
258    new X509CertificateGenerationParameters(
259    validity,
260    extensionBuilder.get().addBasicConstraints(0)
261    .addKeyUsage(EnumSet.of(KeyUsage.keyCertSign,
262    KeyUsage.cRLSign))
263    .build()))
264    .generate(new DistinguishedName(dn), publicKey,
265    new X509CertificateParameters());
266    }
267   
268    /**
269    * Create an end entity certificate.
270    *
271    * @param issuer the certified keypair for issuing the certificate
272    * @param keyPair the keyPair of the public key to certify
273    * @param dn the distinguished name for the new the certificate.
274    * @param validity the validity of the certificate from now in days.
275    * @param subjectAltName the alternative names for the certificate
276    * @return a certified keypair.
277    * @throws IOException in case on error while reading the public key.
278    * @throws GeneralSecurityException in case of error.
279    */
 
280  0 toggle public CertifiedKeyPair issueCertificate(CertifiedKeyPair issuer, AsymmetricKeyPair keyPair, String dn,
281    int validity, List<X509GeneralName> subjectAltName) throws IOException, GeneralSecurityException
282    {
283  0 return new CertifiedKeyPair(
284    keyPair.getPrivate(),
285    issueCertificate(issuer, keyPair.getPublic(), dn, validity, subjectAltName)
286    );
287    }
288   
289    /**
290    * Create an end entity certificate.
291    *
292    * @param privateKey the private key for signing the certificate
293    * @param issuer the certificate of the issuer of the certificate
294    * @param publicKey the public key to certify
295    * @param dn the distinguished name for the new the certificate.
296    * @param validity the validity of the certificate from now in days.
297    * @param subjectAltName the alternative names for the certificate
298    * @return a certified public key.
299    * @throws IOException in case on error while reading the public key.
300    * @throws GeneralSecurityException in case of error.
301    */
 
302  0 toggle public CertifiedPublicKey issueCertificate(PrivateKeyParameters privateKey, CertifiedPublicKey issuer,
303    PublicKeyParameters publicKey, String dn, int validity, List<X509GeneralName> subjectAltName)
304    throws IOException, GeneralSecurityException
305    {
306  0 return issueCertificate(new CertifiedKeyPair(privateKey, issuer), publicKey, dn, validity, subjectAltName);
307    }
308   
309    /**
310    * Create an end entity certificate. By default, the key can be used for encryption and signing. If the end entity
311    * contains some alternate subject names of type X509Rfc822Name a extended email protection usage is added. If the
312    * end entity contains some alternate subject names of type X509DnsName or X509IpAddress extended server and client
313    * authentication usages are added.
314    *
315    * @param issuer the keypair for issuing the certificate
316    * @param publicKey the public key to certify
317    * @param dn the distinguished name for the new the certificate.
318    * @param validity the validity of the certificate from now in days.
319    * @param subjectAltName the alternative names for the certificate
320    * @return a certified public key.
321    * @throws IOException in case on error while reading the public key.
322    * @throws GeneralSecurityException in case of error.
323    */
 
324  0 toggle public CertifiedPublicKey issueCertificate(CertifiedKeyPair issuer, PublicKeyParameters publicKey,
325    String dn, int validity, List<X509GeneralName> subjectAltName) throws IOException, GeneralSecurityException
326    {
327  0 X509CertificateParameters params;
328  0 X509ExtensionBuilder builder = extensionBuilder.get().addKeyUsage(EnumSet.of(KeyUsage.digitalSignature,
329    KeyUsage.dataEncipherment));
330   
331  0 if (subjectAltName != null) {
332  0 params = new X509CertificateParameters(
333    extensionBuilder.get().addSubjectAltName(false, subjectAltName.toArray(new X509GeneralName[]{}))
334    .build());
335  0 Set<String> extUsage = new HashSet<String>();
336  0 for (X509GeneralName genName : subjectAltName) {
337  0 if (genName instanceof X509Rfc822Name) {
338  0 extUsage.add(ExtendedKeyUsages.EMAIL_PROTECTION);
339  0 } else if (genName instanceof X509DnsName || genName instanceof X509IpAddress) {
340  0 extUsage.add(ExtendedKeyUsages.SERVER_AUTH);
341  0 extUsage.add(ExtendedKeyUsages.CLIENT_AUTH);
342    }
343  0 builder.addExtendedKeyUsage(false, new ExtendedKeyUsages(extUsage));
344    }
345    } else {
346  0 params = new X509CertificateParameters();
347    }
348   
349   
350  0 return certificateGeneratorFactory.getInstance(
351    CertifyingSigner.getInstance(true, issuer, signerFactory),
352    new X509CertificateGenerationParameters(validity, builder.build()))
353    .generate(new DistinguishedName(dn), publicKey, params);
354    }
355   
356    /**
357    * Generate a CMS (Cryptographic Message Syntax) signature for a given byte content. The resulting signature
358    * might contains the content itself.
359    *
360    * @param data the data to be signed
361    * @param keyPair the certified key pair used for signing
362    * @param embedContent if true, the signed content is embedded with the signature.
363    * @return the resulting signature encoded ASN.1 and in accordance with RFC 3852.
364    * @throws GeneralSecurityException on error.
365    */
 
366  0 toggle public byte[] cmsSign(byte[] data, CertifiedKeyPair keyPair, boolean embedContent)
367    throws GeneralSecurityException
368    {
369  0 return cmsSign(data, keyPair, null, null, embedContent);
370    }
371   
372    /**
373    * Generate a CMS (Cryptographic Message Syntax) signature for a given byte content. The resulting signature
374    * might contains the content itself and the certificate chain of the key used to sign.
375    *
376    * @param data the data to be signed
377    * @param keyPair the certified key pair used for signing
378    * @param certificateProvider Optionally, a certificate provider for obtaining the chain of certificate to embed.
379    * If null, no certificate are embedded with the signature.
380    * @param embedContent if true, the signed content is embedded with the signature.
381    * @return the resulting signature encoded ASN.1 and in accordance with RFC 3852.
382    * @throws GeneralSecurityException on error.
383    */
 
384  0 toggle public byte[] cmsSign(byte[] data, CertifiedKeyPair keyPair, CertificateProvider certificateProvider,
385    boolean embedContent) throws GeneralSecurityException
386    {
387  0 return cmsSign(data, keyPair, certificateProvider, null, embedContent);
388    }
389   
390    /**
391    * Generate a CMS (Cryptographic Message Syntax) signature for a given byte content. The resulting signature
392    * might contains the content itself and the certificate chain of the key used to sign.
393    *
394    * @param data the data to be signed
395    * @param keyPair the certified key pair used for signing
396    * @param certificateProvider Optionally, a certificate provider for obtaining the chain of certificate to embed.
397    * If null, no certificate are embedded with the signature.
398    * @param existingSignature if not null, a existing signature on the same data that should be kept.
399    * @param embedContent if true, the signed content is embedded with the signature.
400    * @return the resulting signature encoded ASN.1 and in accordance with RFC 3852.
401    * @throws GeneralSecurityException on error.
402    */
 
403  0 toggle public byte[] cmsSign(byte[] data, CertifiedKeyPair keyPair, CertificateProvider certificateProvider,
404    CMSSignedDataVerified existingSignature, boolean embedContent) throws GeneralSecurityException
405    {
406  0 CMSSignedDataGeneratorParameters parameters = new CMSSignedDataGeneratorParameters()
407    .addSigner(CertifyingSigner.getInstance(true, keyPair, signerFactory));
408   
409  0 if (existingSignature != null) {
410  0 for (CMSSignerInfo existingSigner : existingSignature.getSignatures()) {
411  0 parameters.addSignature(existingSigner);
412    }
413    }
414   
415  0 Set<CertifiedPublicKey> certs = new HashSet<CertifiedPublicKey>();
416  0 if (existingSignature != null && existingSignature.getCertificates() != null) {
417  0 certs.addAll(existingSignature.getCertificates());
418    }
419   
420  0 if (certificateProvider != null) {
421  0 if (existingSignature != null) {
422  0 for (CMSSignerInfo existingSigner : existingSignature.getSignatures()) {
423  0 if (existingSigner.getSubjectKeyIdentifier() != null) {
424  0 addCertificateChain(
425    certificateProvider.getCertificate(existingSigner.getSubjectKeyIdentifier()),
426    certificateProvider, certs);
427    } else {
428  0 addCertificateChain(
429    certificateProvider.getCertificate(existingSigner.getIssuer(),
430    existingSigner.getSerialNumber()),
431    certificateProvider, certs);
432    }
433    }
434    }
435   
436  0 addCertificateChain(keyPair.getCertificate(), certificateProvider, certs);
437    }
438   
439  0 if (!certs.isEmpty()) {
440  0 parameters.addCertificates(certs);
441    }
442   
443  0 return cmsSignedDataGenerator.generate(data, parameters, embedContent);
444    }
445   
 
446  0 toggle private void addCertificateChain(CertifiedPublicKey certificate, CertificateProvider certificateProvider,
447    Collection<CertifiedPublicKey> certs)
448    {
449  0 Collection<CertifiedPublicKey> chain = certificateChainBuilder.build(certificate, certificateProvider);
450  0 if (chain != null) {
451  0 certs.addAll(chain);
452    }
453    }
454   
455    /**
456    * Verify a CMS signature with embedded content and containing all the certificate required for validation.
457    *
458    * @param signature the CMS signature to verify. The signature should have the signed content embedded as well as
459    * all the certificates for the signers.
460    * @return result of the verification.
461    * @throws GeneralSecurityException on error.
462    */
 
463  0 toggle public CMSSignedDataVerified cmsVerify(byte[] signature)
464    throws GeneralSecurityException
465    {
466  0 return cmsSignedDataVerifier.verify(signature);
467    }
468   
469    /**
470    * Verify a CMS signature without embedded content but containing all the certificate required for validation.
471    *
472    * @param signature the CMS signature to verify.
473    * @param data the content to verify the signature against, or null of the content is embedded in the signature.
474    * @return a the result of the verification.
475    * @throws GeneralSecurityException on error.
476    */
 
477  0 toggle public CMSSignedDataVerified cmsVerify(byte[] signature, byte[] data)
478    throws GeneralSecurityException
479    {
480  0 return cmsSignedDataVerifier.verify(signature, data);
481    }
482   
483    /**
484    * Verify a CMS signature with embedded content, but requiring external certificates to be validated.
485    *
486    * @param signature the CMS signature to verify.
487    * @param certificateProvider Optionally, a certificate provider for obtaining the chain of certificate for
488    * verifying the signatures. If null, certificat should all be embedded in the signature.
489    * @return a the result of the verification.
490    * @throws GeneralSecurityException on error.
491    */
 
492  0 toggle public CMSSignedDataVerified cmsVerify(byte[] signature, CertificateProvider certificateProvider)
493    throws GeneralSecurityException
494    {
495  0 return cmsSignedDataVerifier.verify(signature, certificateProvider);
496    }
497   
498    /**
499    * Verify a CMS signature without embedded content, and requiring external certificates to be validated.
500    *
501    * @param signature the CMS signature to verify.
502    * @param data the content to verify the signature against, or null of the content is embedded in the signature.
503    * @param certificateProvider Optionally, a certificate provider for obtaining the chain of certificate for
504    * verifying the signatures. If null, certificat should all be embedded in the signature.
505    * @return a the result of the verification.
506    * @throws GeneralSecurityException on error.
507    */
 
508  0 toggle public CMSSignedDataVerified cmsVerify(byte[] signature, byte[] data, CertificateProvider certificateProvider)
509    throws GeneralSecurityException
510    {
511  0 return cmsSignedDataVerifier.verify(signature, data, certificateProvider);
512    }
513   
514    /**
515    * Check that an X509 certificate chain is complete and valid now.
516    *
517    * @param chain the ordered chain of certificate starting from root CA.
518    * @return true if the chain is a X509 certificate chain complete and valid on the given date.
519    */
 
520  0 toggle public boolean checkX509CertificateChainValidity(Collection<CertifiedPublicKey> chain)
521    {
522  0 return checkX509CertificateChainValidity(chain, null);
523    }
524   
525    /**
526    * Check that an X509 certificate chain is complete and is valid on a given date.
527    *
528    * @param chain the ordered chain of certificate starting from root CA.
529    * @param date the date to check the validity for, or null to check for now.
530    * @return true if the chain is a X509 certificate chain complete and valid on the given date.
531    */
 
532  0 toggle public boolean checkX509CertificateChainValidity(Collection<CertifiedPublicKey> chain, Date date)
533    {
534  0 if (chain == null || chain.isEmpty()) {
535  0 return false;
536    }
537   
538  0 Date checkDate = (date != null) ? date : new Date();
539  0 boolean rootExpected = true;
540  0 for (CertifiedPublicKey cert : chain) {
541  0 if (!(cert instanceof X509CertifiedPublicKey)) {
542  0 return false;
543    }
544  0 if (rootExpected) {
545  0 if (!((X509CertifiedPublicKey) cert).isRootCA()) {
546  0 return false;
547    }
548  0 rootExpected = false;
549    }
550  0 if (!((X509CertifiedPublicKey) cert).isValidOn(checkDate)) {
551  0 return false;
552    }
553    }
554  0 return true;
555    }
556    }