bouncycastle fails NTRU generation - java

There are three types of default security levels of NTRU, implemented in bouncy-castle:
1. NTRUSigningKeyGenerationParameters.TEST157
2. NTRUSigningKeyGenerationParameters.APR2011_439
3. NTRUSigningKeyGenerationParameters.APR2011_743
First two are generated normally, but when I try to generate the tird one, I get the next Exception:
SEVERE: Servlet.service() for servlet [mvc-dispatcher] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Signing failed: too many retries (max=100)] with root cause
java.lang.IllegalStateException: Signing failed: too many retries (max=100)
Here is piece of my code:
NTRUSigningPrivateKeyParameters ntruSigningPrivateKeyParameters1 = null;
NTRUSigner ntruSigner = new NTRUSigner(ntruSigningKeyGenerationParameters.getSigningParameters());
try {
ntruSigningPrivateKeyParameters1 = new NTRUSigningPrivateKeyParameters(ntruSigningPrivateKeyParameters.getEncoded(), ntruSigningKeyGenerationParameters);
} catch (IOException e) {
e.printStackTrace();
}
ntruSigner.init(true, ntruSigningPrivateKeyParameters);
byte [] res = ntruSigner.generateSignature();
Calling ntruSigner.generateSignature() with the third set of parameters leads to a such Exception.
Does anyone knows how to solve it?

Currently, it's a bug, so there are two solutions:
use another library - tbuktu's github project (bouncy-castle is using it with some modifications, as I see)
download sources, catch the bug of this generation parameter, solve it and pack into library for a project

It's not really a bug in the code. The problem is that the norm bound in the APR2011_743 and APR2011_743_PROD parameter sets is too low which means that the signer is unable to generate a valid signature.
For N=743, q=2048 and beta=0.127 you should choose a norm bound of around 545 (see equation 10 in J. Hoffstein et al, Performance improvements and a baseline parameter generation algorithm for NTRUSign) but the parameter sets in BouncyCastle use normBound=405. Changing this solves the issue.

Updating the normBound does appear to fix the issue, however I should point out the NTRUSigner class is now deprecated in Bouncy Castle. The NTRU signing algorithm was shown to be badly broken just over a year ago. See:
http://www.di.ens.fr/~ducas/NTRUSign_Cryptanalysis/DucasNguyen_Learning.pdf
for details.

Related

Reading encrypted private key in PKCS#8 format through bouncycastle, Java failing in docker container

I am trying to read a PKCS#8 private key which looks like following:
key.k8 --> (Sample key. Passphrase - 123456):
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQILbKY9hPxYSoCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCvaGt2Hmm2NpHpxbLvHKyOBIIE
0IQ7dVrAGXLZl0exYIvyxLAu6zO00jL6b3sb/agTcCFOz8JU6fBanxY0d5aYO4Dn
mynQG7BoljU470s0zIwW/wk0MmdUFl4nXWBX/4qnG0sZqZ9KZ7I8R/WrBkmpX8C/
4pjdVhu8Ht8dfOYbkbjMBTohDJz8vJ0QwDIXi9yFjjef+QjwrFOl6kAeDJFVMGqc
s7K/wOnhsL1XxfW9uTulPiZh5YTZKcatMkeGDR7c+cg5I+Mutim92diWuCekhNoa
uvhUy1M3cbs7Azp1Mhz+V0CDKklI95EvN4u23WhiJPCjAofC/e45/heOP3Dwm7WZ
zHEY1C/X8PsTl6MEEIF3ZJP+4Vr0corAs1L2FqE6oOng8dFFYmF5eRyBx6bxFd05
iYbfOH24/b3qtFKPC689kGEd0gWp1dwES35SNNK+cJqVRTjgI0oKhOai3rhbGnmp
tx4+JqploQgTorj4w9asbtZ/qZA2mYSSR/Q64SHv7LfoUCI9bgx73MqRQBgvI5yS
b4BoFBnuEgOduZLaGKGjKVW3m5/q8oiDAaspcSLCJMIrdOTYWJB+7mfxX4Xy0vEe
5m2jXpSLQmrfjgpSTpHDKi/3b6OzKOcHjSFBf8IoiHuLc5DVvLECzDUxxaMrTZ71
0YXvEPwl2R9BzEANwwR9ghJvFg1Be/d5W/WA1Efe6cNQNBlmErxD6l+4KDUgGjTr
Aaksp9SZAv8uQAsg7C57NFHpTA5Hznr5JctL+WlO+Gk0cAV6i4Py3kA6EcfatsnS
PqP2KbxT+rb2ATMUZqgWc20QvDt6j0CTA1BuVD1PNhnAUFvb2ocyEEXOra22DPPS
UPu6jirSIyFcjqFjJ9A1FD9L4/UuX2UkDSLqblFlYB1+G55KZp+EKz8SZoN5qXy1
LyMtnacEP5OtRDrOjopzVNiuV1Uv63M9QVi1hZlVLJEomgjWuvuyEuIwDaY2uryW
vx+jJEZyySFkb1JwAbrm+p6sCTFnbQ/URKC2cit/FJyKqNim6VQvGL8Sez34qV3z
D13QJgTZfsy+BaZoaQ6cJTXtJ8cN0IcQciOiDNBKMW66zO6ujS8G+KNviNQypDm6
h4sOgjMqLaZ4ezPEdNj/gaxV7Y15nVRu0re8dVkaa5t9ft/sh6A+yeTD5tS5hHkf
NI7uJPTaTXVoz7xq2PAJUTWujMLMZKtmNOzNqYvxWRy3tCOFobBQkMxqEBEwHd+x
SA+gFcJKJ+aNfCGZJ5fFr8rNlhtOF6uMwOAlfiUlP/pCUDUCKPjZVj4K95yNc8Io
jSZSPb5tGPe0HqXgc6IAfQarlUZt90oVtzL0OfOfTxe1bEzS2ccNadbx/6vjLBc4
q5UuUBppl3rXpbuZ7J1Rp3n2byF4APxFdT2LHKq+MYMfWUToau/TCMT4lFIM9tM8
7TuuyUT2PKzf/xlsl4iScw96z9xxGPQrXn7IA2W5iL+0eCLztJdjNRX1FisdfIBL
PraOVlmF8jHKbFdRZ8Yi8pApbQjvHi24g7dX7u/cq1FH/VE+nJ0O8YVCYVDw13CW
h0p7yD7BuB0R+0WnR0yvkp30vK4/rtCB+Ob8bH/+HvAZrAU5X8jq/wsQbLkrLHZV
6A6GGfX8+hy5AoaXsH1BHnMyXkaF6Mv29z8JcslDJxX/
-----END ENCRYPTED PRIVATE KEY-----
Following code is being used to parse the private key:
InputStream privateKeyInputStream = getPrivateKeyInputStream(); // reads the key file from classpath and share as DataStream
logger.info("InputStreamExists --> {} ", privateKeyInputStream.available());
PEMParser pemParser = new PEMParser(new InputStreamReader(privateKeyInputStream));
Object pemObject = pemParser.readObject();
if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) {
// Handle the case where the private key is encrypted.
PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemObject;
InputDecryptorProvider pkcs8Prov =
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(passphrase.toCharArray());
privateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(pkcs8Prov); // fails here
}
InputStream resourceAsStream = null;
if ("local".equals(privateKeyMode)) {
resourceAsStream = this.getClass().getResourceAsStream(privateKeyPath);
} else {
File keyFile = new File(privateKeyPath);
logger.info(
"Key file found in {} mode. FileName : {}, Exists : {}",
privateKeyMode,
keyFile.getName(),
keyFile.exists());
try {
resourceAsStream = new DataInputStream(new FileInputStream(keyFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
When I am running this code through intelliJ on windows, the code works fine but when I run it through docker container I am getting following exception:
org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: failed to construct sequence from byte[]: Extra data detected in stream
snowflake-report-sync | at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source) ~[bcpkix-jdk15on-1.64.jar!/:1.64.00.0]
snowflake-report-sync | at com.optum.snowflakereportsync.configuration.SnowFlakeConfig.getPrivateKey(SnowFlakeConfig.java:103) ~[classes!/:na]
snowflake-report-sync | at com.optum.snowflakereportsync.configuration.SnowFlakeConfig.getConnectionProperties(SnowFlakeConfig.java:67) ~[classes!/:na]
Following is Dockerfile used:
FROM adoptopenjdk/openjdk11-openj9:latest
COPY build/libs/snowflake-report-sync-*.jar snowflake-report-sync.jar
RUN mkdir /encryption-keys
COPY encryption-keys/ /encryption-keys/ #keys are picked from docker filesystem when running in container
EXPOSE 8080
CMD java -Dcom.sun.management.jmxremote -noverify ${JAVA_OPTS} -jar snowflake-report-sync.jar
Options tried:
Ensured that key file is being read while running in container. Logger "InputStreamExists --> {}" gives number of bytes
Ran dos2unix on key.k8 just to make sure there are no Window's "^M" characters which be could be causing issue as container is linux one : FROM adoptopenjdk/openjdk11-openj9:latest
Not sure what I am doing wrong but any help or pointers would be appreciated.
Like #Bragolgirith suspected, BouncyCastle seems to have problems with OpenJ9. I guess it is not a Docker issue, because I can reproduce it on GitHub Actions, too. It is also not limited to BouncyCastle 1.64 or 1.70, it happens in both versions. It also happens on OpenJ9 JDK 11, 14, 17 on Windows, MacOS and Linux, but for the same matrix of Java and OS versions it works on Adopt-Hotspot and Zulu.
Here is an example Maven project and a failed matrix build. So if you select another JVM type, you should be fine. I know that #Bragolgirith already suggested that, but I wanted to make the problem reproducible for everyone and also provide an MCVE, in case someone wants to open a BC or OpenJ9 issue.
P.S.: It is also not a character set issue with the InputStreamReader. This build fails exactly the same as before after I changed the constructor call.
Update: I have created BC-Java issue #1099. Let's see what the maintainers can say about this.
Update 2: The solution to your problem is to explicitly set the security provider to BC for your input decryptor provider. Thanks to David Hook for his helpful comment in #1099.
BouncyCastleProvider securityProvider = new BouncyCastleProvider();
Security.addProvider(securityProvider);
// (...)
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder()
// Explicitly setting security provider helps to avoid ambiguities
// which otherwise can cause problems, e.g. on OpenJ9 JVMs
.setProvider(securityProvider)
.build(passphrase.toCharArray());
See this commit and the corresponding build, now passing on all platforms, Java versions and JVM types (including OpenJ9).
Because #Bragolgirith mentioned it in his answer: If you want to avoid the explicit new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(securityProvider), the call Security.insertProviderAt(securityProvider, 1) instead of simply Security.addProvider(securityProvider) would in this case also solve the problem. But this holds true only as long as no other part of your code or any third-party library sets another provider to position 1 afterwards, as explained in the Javadoc. So maybe it is not a good idea to rely on that.
Edit:
On second thought, when creating the JceOpenSSLPKCS8DecryptorProviderBuilder, you're not explicitly specifying the provider:
new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME) // add this line
.build(passphrase.toCharArray());
It seems OpenJ9 uses a different provider/algo selection mechanism and selects the SunJCE's AESCipher class as CipherSpi by default, while Hotspot selects BouncyCastleProvider's AES class.
Explicitly specifying the provider should work in all cases.
Alternatively, when adding the BouncyCastleProvider you could insert it at the first preferred position (i.e. Security.insertProviderAt(new BouncyCastleProvider(), 1) instead of Security.addProvider(new BouncyCastleProvider())) so that it gets selected.
(It's still unclear to me why the provider selection mechanism differs between the different JVMs.)
Original post:
I've managed to reproduce the issue and at this point I'd say it's an incompatibility issue with the OpenJ9 JVM.
Starting from a Hotspot base image instead, e.g.
FROM adoptopenjdk:11-jre-hotspot
makes the code work.
(Not yet entirely sure whether the fault lies with the Docker image itself, the OpenJ9 JVM or BouncyCastle)

Java Spring Unsupported StringMatcher REGEX

There is an issue when attempting to use StringMatcher.REGEX with java spring. There is no compiler error or anything, but when you attempt to call the item using the above string matcher, you receive the following error:
2020-10-09 15:07:30.543 ERROR 29584 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Unsupported StringMatcher REGEX; nested exception is java.lang.IllegalArgumentException: Unsupported StringMatcher REGEX] with root cause
java.lang.IllegalArgumentException: Unsupported StringMatcher REGEX
at org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder.getPredicates(QueryByExamplePredicateBuilder.java:210) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE]
at org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder.getPredicate(QueryByExamplePredicateBuilder.java:102) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE]
at org.springframework.data.jpa.repository.support.SimpleJpaRepository$ExampleSpecification.toPredicate(SimpleJpaRepository.java:886) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE]
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.applySpecificationToCriteria(SimpleJpaRepository.java:762) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE]
The error can be seen when executing something along the lines of:
ExampleMatcher matcher = ExampleMatcher
.matchingAll()
.withStringMatcher(
ExampleMatcher.StringMatcher.REGEX
);
Note that the above code compiles just fine, the error happens when it is executed.
First, StringMatcher.REGEX is an entirely valid value. As you can see inside the ExampleMatcher.class file, you have 6 valid StringMatcher options:
public static enum StringMatcher {
DEFAULT,
EXACT,
STARTING,
ENDING,
CONTAINING,
REGEX;
private StringMatcher() {
}
}
Because it is a valid entry, the code will compile just fine and see no issue.
The problem is that the regex option only works in Java, not Java Spring. The spring documentation for query by example states:
Only supports starts/contains/ends/regex matching for strings and exact matching for other property types.
However, this is not the case. According to a jira ticket for Spring, there is no support for StringMatcher.REGEX and there never will be, because not all database systems support it. Basically, you only have access to DEFAULT, EXACT, STARTING, ENDING, and CONTAINING string matcher items and REGEX will never work. I hope this answer saves someone a lot of time. I wasted too much trying to figure it out.

Error loading KeyStore(PKCS12)

I want to load a KeyStore with the load method but when I use it I got an error. I don't understant what ECParameter is.
try {
FileInputStream is =new FileInputStream(new File("D:\\UZ\\key_privateUZ.p12"));
ks = KeyStore.getInstance("PKCS12");
password="1234".toCharArray();
ks.load(is, password);
} catch (Exception e) {
e.printStackTrace();
}
my error:
java.security.cert.CertificateParsingException: java.io.IOException: Only named ECParameters supported
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:171)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1788)
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:202)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:97)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
at sun.security.pkcs12.PKCS12KeyStore.loadSafeContents(PKCS12KeyStore.java:1441)
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1313)
at java.security.KeyStore.load(KeyStore.java:1214)
at Main.main(Main.java:21) Caused by: java.io.IOException: Only named ECParameters supported
at sun.security.ec.ECParameters.decodeParameters(ECParameters.java:202)
at sun.security.ec.ECParameters.engineInit(ECParameters.java:319)
at java.security.AlgorithmParameters.init(AlgorithmParameters.java:293)
at sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:139)
at sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:114)
at sun.security.x509.AlgorithmId.parse(AlgorithmId.java:381)
at sun.security.x509.X509Key.parse(X509Key.java:168)
at sun.security.x509.CertificateX509Key.<init>(CertificateX509Key.java:75)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:705)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:169)
... 8 more
Regars
Wazol
You'll find plenty of information on EC (elliptic curve) parameters if you do a search. In particular, the JDK Code which throws that exception.
If you look in the relevant method, you will see a commented out section after the exception which indicates:
It is left as a starting point for a complete parsing implementation
In other words the JDK doesn't support all possible encodings.
You might have better luck using Bouncy Castle which generally has more features than the basic JDK.
It might also help if you explain how the data in the keystore was generated.
I solved this problem like this
In ECKey class
ECParameterSpec papamSpec = ECNamedCurveTable.getParameterSpec(SECP);

JCO transmits erroneous parameters to "CSAP_MAT_BOM_MAINTAIN"

I am trying to call the function module "CSAP_MAT_BOM_MAINTAIN" to create a BOM in SAP but i get error.
IFunctionTemplate ft = mRepository.getFunctionTemplate("CSAP_MAT_BOM_MAINTAIN");
System.out.println(" Functional Template Created ");
if (ft == null){return;}
JCO.Function function = ft.getFunction();
JCO.ParameterList importparams =function.getImportParameterList();
// Setting HeadData Structure Information
//importparams.setValue("C000000609", "CHANGE_NO");
importparams.setValue("CPF10104", "MATERIAL");
importparams.setValue("1", "BOM_USAGE");
importparams.setValue("0001", "PLANT");
importparams.setValue("01", "ALTERNATIVE");
importparams.setValue("11.11.2011", "VALID_FROM");
importparams.setValue("X", "FL_COMMIT_AND_WAIT");
importparams.setValue("X", "FL_BOM_CREATE");
importparams.setValue("X", "FL_NEW_ITEM");
importparams.setValue("X", "FL_COMPLETE");
importparams.setValue("X", "FL_DEFAULT_VALUES");
JCO.Structure headStructure = importparams.getStructure("I_STKO");
headStructure.setValue("01", "BOM_STATUS");
headStructure.setValue("1", "BASE_QUAN");
headStructure.setValue("KG", "BASE_UNIT");
headStructure.setValue("BOM01", "BOM_GROUP");
JCO.Table stpo = function.getTableParameterList().getTable("T_STPO");
stpo.appendRow();
stpo.setValue("BOM Position 2.1", "ITEM_TEXT1");
stpo.setValue("L", "ITEM_CATEG");
stpo.setValue("L", "ID_ITM_CTG");
stpo.setValue("0010", "ITEM_NO");
stpo.setValue("0010", "ID_ITEM_NO");
stpo.setValue("13", "COMP_QTY");
stpo.setValue("KG", "COMP_UNIT");
stpo.setValue("00000001", "ITEM_NODE");
stpo.setValue("00000001", "ITEM_COUNT");
stpo.setValue("000000000000000000", "DEP_LINK");
stpo.setValue("12345-R6000001", "COMPONENT");
//stpo.setValue("12345-R6000001", "ID_COMP");
JCO.Table stpu = function.getTableParameterList().getTable("T_STPU");
stpu.appendRow();
stpu.setValue("0", "POINTER");
stpu.setValue("00000000", "STLKN");
stpu.setValue("0010", "STPOZ");
stpu.setValue("0001", "UPOSZ");
stpu.setValue("46", "UPMNG");
stpu.setValue("T1", "EBORT");
I get error:
Exception in thread "main" com.sap.mw.jco.JCO$AbapException: (126) ERROR: Terminate processing.
After searching over the internet i found that this error comes when we have wrong input params.
But i am unable to find the error.
Please note that i have limited knowledge about ABAP programming.
Can any one help me?
(126) ABAP EXCEPTION: An exception has been thrown by a function module in the remote system.
I think you should carefully review all of these new parameters, since one is causing a bug in your function.
If you have access to the SAP system, you can run transaction ST22 to get a detailed error log. However, you may need to de-bug within SAP as per Raj's suggestion.

Problem validating against an XSD with Java5

I'm trying to validate an Atom feed with Java 5 (JRE 1.5.0 update 11). The code I have works without problem in Java 6, but fails when running in Java 5 with a
org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'xml:base' to a(n) 'attribute declaration' component.
I think I remember reading something about the version of Xerces bundled with Java 5 having some problems with some schemas, but i cant find the workaround. Is it a known problem ? Do I have some error in my code ?
public static void validate() throws SAXException, IOException {
List<Source> schemas = new ArrayList<Source>();
schemas.add(new StreamSource(AtomValidator.class.getResourceAsStream("/atom.xsd")));
schemas.add(new StreamSource(AtomValidator.class.getResourceAsStream("/dc.xsd")));
// Lookup a factory for the W3C XML Schema language
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
// Compile the schemas.
Schema schema = factory.newSchema(schemas.toArray(new Source[schemas.size()]));
Validator validator = schema.newValidator();
// load the file to validate
Source source = new StreamSource(AtomValidator.class.getResourceAsStream("/sample-feed.xml"));
// check the document
validator.validate(source);
}
Update : I tried the method below, but I still have the same problem if I use Xerces 2.9.0. I also tried adding xml.xsd to the list of schemas (as xml:base is defined in xml.xsd) but this time I have
Exception in thread "main" org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'null', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Update 2: I tried to configure a proxy with the VM arguments -Dhttp.proxyHost=<proxy.host.com> -Dhttp.proxyPort=8080 and now it works. I'll try to post a "real answer" from home.
and sorry, I cant reply as a comment : because of security reasons XHR is disabled from work ...
Indeed, people have been mentioning the Java 5 Sun provided SchemaFactory is giving troubles.
So: did you include Xerces in your project yourself?
After including Xerces, you need to ensure it is being used. If you like to hardcode it (well, as a minimal requirement you'd probably use some application properties file to enable and populate the following code):
String schemaFactoryProperty =
"javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI;
System.setProperty(schemaFactoryProperty,
"org.apache.xerces.jaxp.validation.XMLSchemaFactory");
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Or, if you don't want to hardcode, or when your troublesome code would be in some 3rd party library that you cannot change, set it on the java command line or environment options. For example (on one line of course):
set JAVA_OPTS =
"-Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema
=org.apache.xerces.jaxp.validation.XMLSchemaFactory"
By the way: apart from the Sun included SchemaFactory implementation giving trouble (something like com.sun.org.apache.xerces.internal.jaxp.validation.xs.schemaFactoryImpl), it also seems that the "discovery" of non-JDK implementations fails in that version. If I understand correctly than, normally, just including Xerces would in fact make SchemaFactory#newInstance find that included library, and give it precedence over the Sun implementation. To my knowledge, that fails as well in Java 5, making the above configuration required.
I tried to configure a proxy with the VM arguments -Dhttp.proxyHost=<proxy.host.com> -Dhttp.proxyPort=8080 and now it works.
Ah, I didn't realize that xml.xsd is in fact the one referenced as http://www.w3.org/2001/xml.xsd or something like that. That should teach us to always show some XML and XSD fragments as well. ;-)
So, am I correct to assume that 1.) to fix the Java 5 issue, you still needed to include Xerces and set the system property, and that 2.) you did not have xml.xsd available locally?
Before you found your solution, did you happen to try using getResource rather than getResourceAsStream, to see if the exception would then have showed you some more details?
If you actually did have xml.xsd available (so: if getResource did in fact yield a URL) then I wonder what Xerces was trying to fetch from the internet then. Or maybe you did not add that schema to the list prior to adding your own schemas? The order is important: dependencies must be added first.
For whoever gets tot his question using the search: maybe using a custom EntityResolver could have indicated the source of the problem as well (if only writing something to the log and just returning null to tell Xerces to use the default behavior).
Hmmm, just read your "comment" -- editing does not alert people for new replies, so time to ask your boss for some iPhone or some other gadget that is connected to the net directly ;-)
Well, I assume you added:
schemas.add(
new StreamSource(AtomValidator.class.getResourceAsStream("/xml.xsd")));
If so, is xml.xsd actually to be found on the classpath then? I wonder if the getResourceAsStream did not yield null in your case, and how new StreamSource(null) would act then.
Even if getResourceAsStream did not yield null, the resulting StreamSource would still not know where it was loaded from, which may be a problem when trying to include references. So, what if you use the constructor StreamSource(String systemId) instead:
schemas.add(new StreamSource(AtomValidator.class.getResource("/atom.xsd")));
schemas.add(new StreamSource(AtomValidator.class.getResource("/dc.xsd")));
You might also use StreamSource(InputStream inputStream, String systemId), but I don't see any advantage over the above two lines. However, the documentation explains why passing the systemId in either of the 2 constructors seems good:
This constructor allows the systemID to be set in addition to the input stream, which allows relative URIs to be processed.
Likewise, setSystemId(String systemId) explains a bit:
The system identifier is optional if there is a byte stream or a character stream, but it is still useful to provide one, since the application can use it to resolve relative URIs and can include it in error messages and warnings (the parser will attempt to open a connection to the URI only if there is no byte stream or character stream specified).
If this doesn't work out, then maybe some custom error handler can give you more details:
ErrorHandlerImpl errorHandler = new ErrorHandlerImpl();
validator.setErrorHandler(errorHandler);
:
:
validator.validate(source);
if(errorHandler.hasErrors()){
LOG.error(errorHandler.getMessages());
throw new [..];
}
if(errorHandler.hasWarnings()){
LOG.warn(errorHandler.getMessages());
}
...using the following ErrorHandler to capture the validation errors and continue parsing as far as possible:
import org.xml.sax.helpers.DefaultHandler;
private class ErrorHandlerImpl extends DefaultHandler{
private String messages = "";
private boolean validationError = false;
private boolean validationWarning = false;
public void error(SAXParseException exception) throws SAXException{
messages += "Error: " + exception.getMessage() + "\n";
validationError = true;
}
public void fatalError(SAXParseException exception) throws SAXException{
messages += "Fatal: " + exception.getMessage();
validationError = true;
}
public void warning(SAXParseException exception) throws SAXException{
messages += "Warn: " + exception.getMessage();
validationWarning = true;
}
public boolean hasErrors(){
return validationError;
}
public boolean hasWarnings(){
return validationWarning;
}
public String getMessages(){
return messages;
}
}

Categories

Resources