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

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

commit 495bff88dd39cd8c2d654de6bb8914313a7bc6d1
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Jul 12 16:34:43 2019 +0100

    Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63524 part 2 of 2
    
    Enable PKCS#1 format private keys to be read into the in-memory key
    store.
---
 java/org/apache/tomcat/util/buf/Asn1Parser.java    | 89 ++++++++++++++++++++++
 .../apache/tomcat/util/buf/LocalStrings.properties |  3 +
 .../tomcat/util/net/jsse/LocalStrings.properties   |  2 +
 java/org/apache/tomcat/util/net/jsse/PEMFile.java  | 54 +++++++++++--
 webapps/docs/changelog.xml                         | 11 +++
 5 files changed, 154 insertions(+), 5 deletions(-)

diff --git a/java/org/apache/tomcat/util/buf/Asn1Parser.java 
b/java/org/apache/tomcat/util/buf/Asn1Parser.java
new file mode 100644
index 0000000..35fe161
--- /dev/null
+++ b/java/org/apache/tomcat/util/buf/Asn1Parser.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import java.math.BigInteger;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * This is a very basic ASN.1 parser that provides the limited functionality
+ * required by Tomcat. It is a long way from a complete parser.
+ *
+ * TODO: Consider extending this parser and refactoring the SpnegoTokenFixer to
+ *       use it.
+ */
+public class Asn1Parser {
+
+    private static final StringManager sm = 
StringManager.getManager(Asn1Parser.class);
+
+    private final byte[] source;
+
+    private int pos = 0;
+
+
+    public Asn1Parser(byte[] source) {
+        this.source = source;
+    }
+
+
+    public void parseTag(int tag) {
+        int value = next();
+        if (value != tag) {
+            throw new 
IllegalArgumentException(sm.getString("asn1Parser.tagMismatch",
+                    Integer.valueOf(tag), Integer.valueOf(value)));
+        }
+    }
+
+
+    public void parseFullLength() {
+        int len = parseLength();
+        if (len + pos != source.length) {
+            throw new 
IllegalArgumentException(sm.getString("asn1Parser.lengthInvalid",
+                    Integer.valueOf(len), Integer.valueOf(source.length - 
pos)));
+        }
+    }
+
+
+    public int parseLength() {
+        int len = next();
+        if (len > 127) {
+            int bytes = len - 128;
+            len = 0;
+            for (int i = 0; i < bytes; i++) {
+                len = len << 8;
+                len = len + next();
+            }
+        }
+        return len;
+    }
+
+
+    public BigInteger parseInt() {
+        parseTag(0x02);
+        int len = parseLength();
+        byte[] val = new byte[len];
+        System.arraycopy(source, pos, val, 0, len);
+        pos += len;
+        return new BigInteger(val);
+    }
+
+
+    private int next() {
+        return source[pos++] & 0xFF;
+    }
+}
diff --git a/java/org/apache/tomcat/util/buf/LocalStrings.properties 
b/java/org/apache/tomcat/util/buf/LocalStrings.properties
index d204f12..9f2c9fa 100644
--- a/java/org/apache/tomcat/util/buf/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/buf/LocalStrings.properties
@@ -13,6 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+asn1Parser.lengthInvalid=Invalid length [{0}] bytes reported when the input 
data length is [{1}] bytes
+asn1Parser.tagMismatch=Expected to find value [{0}] but found value [{1}]
+
 b2cConverter.unknownEncoding=The character encoding [{0}] is not supported
 
 byteBufferUtils.cleaner=Cannot use direct ByteBuffer cleaner, memory leaking 
may occur
diff --git a/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties 
b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
index 931410a..2bcb836 100644
--- a/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
@@ -35,3 +35,5 @@ jsseUtil.noCrlSupport=The truststoreProvider [{0}] does not 
support the certific
 jsseUtil.noVerificationDepth=The truststoreProvider [{0}] does not support the 
certificateVerificationDepth configuration option
 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
diff --git a/java/org/apache/tomcat/util/net/jsse/PEMFile.java 
b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
index f373939..add1be5 100644
--- a/java/org/apache/tomcat/util/net/jsse/PEMFile.java
+++ b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
@@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
@@ -32,6 +33,7 @@ import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -41,6 +43,7 @@ import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 
+import org.apache.tomcat.util.buf.Asn1Parser;
 import org.apache.tomcat.util.codec.binary.Base64;
 import org.apache.tomcat.util.file.ConfigFileLoader;
 import org.apache.tomcat.util.res.StringManager;
@@ -100,10 +103,13 @@ public class PEMFile {
         for (Part part : parts) {
             switch (part.type) {
                 case "PRIVATE KEY":
-                    privateKey = part.toPrivateKey(null, keyAlgorithm);
+                    privateKey = part.toPrivateKey(null, keyAlgorithm, 
Format.PKCS8);
                     break;
                 case "ENCRYPTED PRIVATE KEY":
-                    privateKey = part.toPrivateKey(password, keyAlgorithm);
+                    privateKey = part.toPrivateKey(password, keyAlgorithm, 
Format.PKCS8);
+                    break;
+                case "RSA PRIVATE KEY":
+                    privateKey = part.toPrivateKey(null, keyAlgorithm, 
Format.PKCS1);
                     break;
                 case "CERTIFICATE":
                 case "X509 CERTIFICATE":
@@ -129,11 +135,21 @@ public class PEMFile {
             return (X509Certificate) factory.generateCertificate(new 
ByteArrayInputStream(decode()));
         }
 
-        public PrivateKey toPrivateKey(String password, String keyAlgorithm) 
throws GeneralSecurityException, IOException {
-            KeySpec keySpec;
+        public PrivateKey toPrivateKey(String password, String keyAlgorithm, 
Format format)
+                throws GeneralSecurityException, IOException {
+            KeySpec keySpec = null;
 
             if (password == null) {
-                keySpec = new PKCS8EncodedKeySpec(decode());
+                switch (format) {
+                    case PKCS1: {
+                        keySpec = parsePKCS1(decode());
+                        break;
+                    }
+                    case PKCS8: {
+                        keySpec = new PKCS8EncodedKeySpec(decode());
+                        break;
+                    }
+                }
             } else {
                 EncryptedPrivateKeyInfo privateKeyInfo = new 
EncryptedPrivateKeyInfo(decode());
                 SecretKeyFactory secretKeyFactory = 
SecretKeyFactory.getInstance(privateKeyInfo.getAlgName());
@@ -164,5 +180,33 @@ public class PEMFile {
 
             throw exception;
         }
+
+
+        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
+            p.parseTag(0x30);
+            // Length
+            p.parseFullLength();
+
+            BigInteger version = p.parseInt();
+            if (version.intValue() == 1) {
+                // JRE doesn't provide a suitable constructor for multi-prime
+                // keys
+                throw new 
IllegalArgumentException(sm.getString("pemFile.noMultiPrimes"));
+            }
+            return new RSAPrivateCrtKeySpec(p.parseInt(), p.parseInt(), 
p.parseInt(), p.parseInt(),
+                    p.parseInt(), p.parseInt(), p.parseInt(), p.parseInt());
+        }
+    }
+
+
+    private enum Format {
+        PKCS1,
+        PKCS8
     }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f784486..32774ff 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -58,6 +58,17 @@
       </fix>
      </changelog>
   </subsection>
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        <bug>63524</bug>: Improve the handling of PEM file based keys and
+        certificates that do not include a full certificate chain when
+        configuring the internal, in-memory key store. Improve the handling of
+        PKCS#1 formatted private keys when configuring the internal, in-memory
+        key store. (markt)
+      </fix>
+    </changelog>
+  </subsection>
   <subsection name="Other">
     <changelog>
       <update>


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

Reply via email to