You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2020/01/09 17:59:15 UTC

[tomcat] branch master updated (b978b2c -> 920ee08)

This is an automated email from the ASF dual-hosted git repository.

markt pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git.


    from b978b2c  Allow more quoted tokens for RewriteValve config
     new 63310d1  Add support for RFC 5915, unencrypted EC key files with JSSE based TLS
     new 920ee08  Silence checkstyle warnings

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 java/org/apache/tomcat/util/buf/Asn1Parser.java    |  6 ++
 java/org/apache/tomcat/util/buf/Asn1Writer.java    | 95 ++++++++++++++++++++++
 .../tomcat/util/net/jsse/LocalStrings.properties   |  3 +-
 java/org/apache/tomcat/util/net/jsse/PEMFile.java  | 94 ++++++++++++++++++++-
 .../valves/rewrite/TestQuotedStringTokenizer.java  | 12 +--
 webapps/docs/changelog.xml                         |  4 +
 6 files changed, 205 insertions(+), 9 deletions(-)
 create mode 100644 java/org/apache/tomcat/util/buf/Asn1Writer.java


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/02: Add support for RFC 5915, unencrypted EC key files with JSSE based TLS

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 63310d1432d3e2216978c19ee577981a3c6a73da
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Jan 9 17:54:04 2020 +0000

    Add support for RFC 5915, unencrypted EC key files with JSSE based TLS
---
 java/org/apache/tomcat/util/buf/Asn1Parser.java    |  6 ++
 java/org/apache/tomcat/util/buf/Asn1Writer.java    | 95 ++++++++++++++++++++++
 .../tomcat/util/net/jsse/LocalStrings.properties   |  3 +-
 java/org/apache/tomcat/util/net/jsse/PEMFile.java  | 94 ++++++++++++++++++++-
 webapps/docs/changelog.xml                         |  4 +
 5 files changed, 199 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/tomcat/util/buf/Asn1Parser.java b/java/org/apache/tomcat/util/buf/Asn1Parser.java
index 35fe161..e8e5727 100644
--- a/java/org/apache/tomcat/util/buf/Asn1Parser.java
+++ b/java/org/apache/tomcat/util/buf/Asn1Parser.java
@@ -83,6 +83,12 @@ public class Asn1Parser {
     }
 
 
+    public void parseBytes(byte[] dest) {
+        System.arraycopy(source, pos, dest, 0, dest.length);
+        pos += dest.length;
+    }
+
+
     private int next() {
         return source[pos++] & 0xFF;
     }
diff --git a/java/org/apache/tomcat/util/buf/Asn1Writer.java b/java/org/apache/tomcat/util/buf/Asn1Writer.java
new file mode 100644
index 0000000..f485517
--- /dev/null
+++ b/java/org/apache/tomcat/util/buf/Asn1Writer.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+public class Asn1Writer {
+
+    public static byte[] writeSequence(byte[]... components) {
+        int len = 0;
+        for (byte[] component : components) {
+            len += component.length;
+        }
+
+        byte[] combined = new byte[len];
+        int pos = 0;
+        for (byte[] component : components) {
+            System.arraycopy(component, 0, combined, pos, component.length);
+            pos += component.length;
+        }
+
+        return writeTag((byte) 0x30, combined);
+    }
+
+
+    public static byte[] writeInteger(int value) {
+        // How many bytes required to write the value? No more than 4 for int.
+        int valueSize = 1;
+        while ((value >> (valueSize * 8)) > 0) {
+            valueSize++;
+        }
+
+        byte[] valueBytes = new byte[valueSize];
+        int i = 0;
+        while (valueSize > 0) {
+            valueBytes[i] = (byte) (value >> (8 * (valueSize - 1)));
+            value = value >> 8;
+            valueSize--;
+            i++;
+        }
+
+        return writeTag((byte) 0x02, valueBytes);
+    }
+
+    public static byte[] writeOctetString(byte[] data) {
+        return writeTag((byte) 0x04, data);
+    }
+
+    public static byte[] writeTag(byte tagId, byte[] data) {
+        int dataSize = data.length;
+        // How many bytes to write the length?
+        int lengthSize = 1;
+        if (dataSize >127) {
+            // 1 byte we have is now used to record how many bytes we need to
+            // record a length > 127
+            // Result is lengthSize = 1 + number of bytes to record length
+            do {
+                lengthSize++;
+            }
+            while ((dataSize >> (lengthSize * 8)) > 0);
+        }
+
+        // 1 for tag + lengthSize + dataSize
+        byte[] result = new byte[1 + lengthSize + dataSize];
+        result[0] = tagId;
+        if (dataSize < 128) {
+            result[1] = (byte) dataSize;
+        } else {
+            // lengthSize is 1 + number of bytes for length
+            result[1] = (byte) (127 + lengthSize);
+            int i = lengthSize;
+            while (dataSize > 0) {
+                result[i] = (byte) (dataSize & 0xFF);
+                dataSize = dataSize >> 8;
+                i--;
+            }
+        }
+
+        System.arraycopy(data, 0, result, 1 + lengthSize, data.length);
+
+        return result;
+    }
+}
diff --git a/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
index 0f9b29e..a129ca1 100644
--- a/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
@@ -31,4 +31,5 @@ jsseUtil.noVerificationDepth=The truststoreProvider [{0}] does not support the c
 jsseUtil.trustedCertNotChecked=The validity dates of the trusted certificate with alias [{0}] were not checked as the certificate was of an unknown type
 jsseUtil.trustedCertNotValid=The trusted certificate with alias [{0}] and DN [{1}] is not valid due to [{2}]. Certificates signed by this trusted certificate WILL be accepted
 
-pemFile.noMultiPrimes=The PKCS#1 certificate is in multi-prime format and Java does not provide an API for constructing an RSA private key object from that format
\ No newline at end of file
+pemFile.noMultiPrimes=The PKCS#1 certificate is in multi-prime format and Java does not provide an API for constructing an RSA private key object from that format
+pemFile.notValidRFC5915=The provided key file does not conform to RFC 5915
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/net/jsse/PEMFile.java b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
index 47c8787..a0962e7 100644
--- a/java/org/apache/tomcat/util/net/jsse/PEMFile.java
+++ b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
@@ -44,6 +44,7 @@ import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 
 import org.apache.tomcat.util.buf.Asn1Parser;
+import org.apache.tomcat.util.buf.Asn1Writer;
 import org.apache.tomcat.util.codec.binary.Base64;
 import org.apache.tomcat.util.file.ConfigFileLoader;
 import org.apache.tomcat.util.res.StringManager;
@@ -57,6 +58,9 @@ public class PEMFile {
 
     private static final StringManager sm = StringManager.getManager(PEMFile.class);
 
+    private static final byte[] OID_EC_PUBLIC_KEY =
+            new byte[] { 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 };
+
     private String filename;
     private List<X509Certificate> certificates = new ArrayList<>();
     private PrivateKey privateKey;
@@ -105,6 +109,9 @@ public class PEMFile {
                 case "PRIVATE KEY":
                     privateKey = part.toPrivateKey(null, keyAlgorithm, Format.PKCS8);
                     break;
+                case "EC PRIVATE KEY":
+                    privateKey = part.toPrivateKey(null, "EC", Format.RFC5915);
+                    break;
                 case "ENCRYPTED PRIVATE KEY":
                     privateKey = part.toPrivateKey(password, keyAlgorithm, Format.PKCS8);
                     break;
@@ -149,6 +156,10 @@ public class PEMFile {
                         keySpec = new PKCS8EncodedKeySpec(decode());
                         break;
                     }
+                    case RFC5915: {
+                        keySpec = new PKCS8EncodedKeySpec(rfc5915ToPkcs8(decode()));
+                        break;
+                    }
                 }
             } else {
                 EncryptedPrivateKeyInfo privateKeyInfo = new EncryptedPrivateKeyInfo(decode());
@@ -182,13 +193,91 @@ public class PEMFile {
         }
 
 
+        /*
+         * RFC5915: SEQ
+         *           INT               value = 1
+         *           OCTET STRING      len = 32 bytes
+         *           [0]
+         *             OID             named EC
+         *           [1]
+         *             BIT STRING      len = 520 bits
+         *
+         * PKCS8:   SEQ
+         *           INT               value = 0
+         *           SEQ
+         *             OID             1.2.840.10045.2.1 (EC public key)
+         *             OID             named EC
+         *           OCTET STRING
+         *             SEQ
+         *               INT           value = 1
+         *               OCTET STRING  len = 32 bytes
+         *               [1]
+         *                 BIT STRING  len = 520 bits
+         *
+         */
+        private byte[] rfc5915ToPkcs8(byte[] source) {
+            // Parse RFC 5915 format EC private key
+            Asn1Parser p = new Asn1Parser(source);
+
+            // Type (sequence)
+            p.parseTag(0x30);
+            // Length
+            p.parseFullLength();
+
+            // Version
+            BigInteger version = p.parseInt();
+            if (version.intValue() != 1) {
+                throw new IllegalArgumentException(sm.getString("pemFile.notValidRFC5915"));
+            }
+
+            // Private key
+            p.parseTag(0x04);
+            int privateKeyLen = p.parseLength();
+            byte[] privateKey = new byte[privateKeyLen];
+            p.parseBytes(privateKey);
+
+            // [0] OID
+            p.parseTag(0xA0);
+            int oidLen = p.parseLength();
+            byte[] oid = new byte[oidLen];
+            p.parseBytes(oid);
+            if (oid[0] != 0x06) {
+                throw new IllegalArgumentException(sm.getString("pemFile.notValidRFC5915"));
+            }
+
+            // [1] Public key
+            p.parseTag(0xA1);
+            int publicKeyLen = p.parseLength();
+            byte[] publicKey = new byte[publicKeyLen];
+            p.parseBytes(publicKey);
+            if (publicKey[0] != 0x03) {
+                throw new IllegalArgumentException(sm.getString("pemFile.notValidRFC5915"));
+            }
+
+
+            // Write out PKCS#8 format
+            return Asn1Writer.writeSequence(
+                    Asn1Writer.writeInteger(0),
+                    Asn1Writer.writeSequence(
+                            OID_EC_PUBLIC_KEY,
+                            oid),
+                    Asn1Writer.writeOctetString(
+                            Asn1Writer.writeSequence(
+                                    Asn1Writer.writeInteger(1),
+                                    Asn1Writer.writeOctetString(privateKey),
+                                    Asn1Writer.writeTag((byte) 0xA1, publicKey))
+                            )
+                    );
+        }
+
+
         private RSAPrivateCrtKeySpec parsePKCS1(byte[] source) {
             Asn1Parser p = new Asn1Parser(source);
 
             // https://en.wikipedia.org/wiki/X.690#BER_encoding
             // https://tools.ietf.org/html/rfc8017#page-55
 
-            // Type
+            // Type (sequence)
             p.parseTag(0x30);
             // Length
             p.parseFullLength();
@@ -207,6 +296,7 @@ public class PEMFile {
 
     private enum Format {
         PKCS1,
-        PKCS8
+        PKCS8,
+        RFC5915
     }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6e400de..921e03e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -125,6 +125,10 @@
         <bug>64007</bug>: Cancel selection key in poller before wrapper close to
         avoid possible deadlock. (remm)
       </fix>
+      <add>
+        Add support for RFC 5915 formatted, unencrypted EC key files when using
+        a JSSE based TLS connector. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/02: Silence checkstyle warnings

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 920ee0899343ca98dd2e57382627a5d3ea74eda2
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Jan 9 17:58:30 2020 +0000

    Silence checkstyle warnings
---
 .../catalina/valves/rewrite/TestQuotedStringTokenizer.java   | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java b/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java
index 59d63fb..ec228ae 100644
--- a/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java
+++ b/test/org/apache/catalina/valves/rewrite/TestQuotedStringTokenizer.java
@@ -16,15 +16,15 @@
  */
 package org.apache.catalina.valves.rewrite;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import org.hamcrest.CoreMatchers;
+
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -60,12 +60,12 @@ public class TestQuotedStringTokenizer {
         List<String> result = new ArrayList<>();
         int count = tokens.size();
         while (tokenizer.hasMoreTokens()) {
-            assertThat(tokenizer.countTokens(), is(count));
+            Assert.assertThat(Integer.valueOf(tokenizer.countTokens()), CoreMatchers.is(Integer.valueOf(count)));
             result.add(tokenizer.nextToken());
             count--;
         }
-        assertThat(tokenizer.countTokens(), is(0));
-        assertThat(tokens, is(result));
+        Assert.assertThat(Integer.valueOf(tokenizer.countTokens()), CoreMatchers.is(Integer.valueOf(0)));
+        Assert.assertThat(tokens, CoreMatchers.is(result));
     }
 
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org