mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-839: Use BouncyCastle for retrieval of certificate details.
Java's build-in support for reading X.509 certificates does not deal well with PIV certificates containing the username as a "serialNumber" attribute. Rather than exposing the string value of that attribute, the Java implementation exposes a byte array that does not fully match the string value shown by a tool like OpenSSL. BouncyCastle, on the other hand, _does_ match the output of OpenSSL, and provides a predictable means of decoding the certificate.
This commit is contained in:
20
doc/licenses/bouncycastle-pkix-fips-1.0.7/LICENSE
Normal file
20
doc/licenses/bouncycastle-pkix-fips-1.0.7/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2000 - 2021 The Legion of the Bouncy Castle Inc.
|
||||||
|
(https://www.bouncycastle.org)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
8
doc/licenses/bouncycastle-pkix-fips-1.0.7/README
Normal file
8
doc/licenses/bouncycastle-pkix-fips-1.0.7/README
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
BouncyCastle PKIX APIs, FIPS Distribution (https://www.bouncycastle.org/fips-java)
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Version: 1.0.7
|
||||||
|
From: 'The Legion of Bouncy Castle' (https://www.bouncycastle.org)
|
||||||
|
License(s):
|
||||||
|
MIT (bundled/bouncycastle-pkix-fips-1.0.7/LICENSE)
|
||||||
|
|
@@ -0,0 +1 @@
|
|||||||
|
org.bouncycastle:bcpkix-fips:jar:1.0.7
|
@@ -119,6 +119,13 @@
|
|||||||
<artifactId>jsr311-api</artifactId>
|
<artifactId>jsr311-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Use FIPS variant of Bouncy Castle crypto library -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-fips</artifactId>
|
||||||
|
<version>1.0.7</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@@ -19,17 +19,14 @@
|
|||||||
package org.apache.guacamole.auth.ssl;
|
package org.apache.guacamole.auth.ssl;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.Principal;
|
import java.util.Date;
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.naming.InvalidNameException;
|
import javax.naming.InvalidNameException;
|
||||||
@@ -46,9 +43,14 @@ import javax.ws.rs.core.UriBuilder;
|
|||||||
import org.apache.guacamole.GuacamoleClientException;
|
import org.apache.guacamole.GuacamoleClientException;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
|
import org.apache.guacamole.GuacamoleServerException;
|
||||||
import org.apache.guacamole.auth.ssl.conf.ConfigurationService;
|
import org.apache.guacamole.auth.ssl.conf.ConfigurationService;
|
||||||
import org.apache.guacamole.auth.sso.NonceService;
|
import org.apache.guacamole.auth.sso.NonceService;
|
||||||
import org.apache.guacamole.auth.sso.SSOResource;
|
import org.apache.guacamole.auth.sso.SSOResource;
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import org.bouncycastle.asn1.x500.style.RFC4519Style;
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder;
|
||||||
|
import org.bouncycastle.openssl.PEMParser;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -247,27 +249,25 @@ public class SSLClientAuthenticationResource extends SSOResource {
|
|||||||
public String getUsername(byte[] certificate) throws GuacamoleException {
|
public String getUsername(byte[] certificate) throws GuacamoleException {
|
||||||
|
|
||||||
// Parse and re-verify certificate is valid with respect to timestamps
|
// Parse and re-verify certificate is valid with respect to timestamps
|
||||||
X509Certificate cert;
|
X509CertificateHolder cert;
|
||||||
try (InputStream input = new ByteArrayInputStream(certificate)) {
|
try (Reader reader = new StringReader(new String(certificate, StandardCharsets.UTF_8))) {
|
||||||
|
|
||||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
PEMParser parser = new PEMParser(reader);
|
||||||
cert = (X509Certificate) certFactory.generateCertificate(input);
|
cert = (X509CertificateHolder) parser.readObject();
|
||||||
|
|
||||||
// Verify certificate is valid (it should be given pre-validation
|
// Verify certificate is valid (it should be given pre-validation
|
||||||
// from SSL termination, but it's worth rechecking for sanity)
|
// from SSL termination, but it's worth rechecking for sanity)
|
||||||
cert.checkValidity();
|
if (!cert.isValidOn(new Date()))
|
||||||
|
throw new GuacamoleClientException("Certificate has expired.");
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (CertificateException e) {
|
|
||||||
throw new GuacamoleClientException("Certificate is not valid: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new GuacamoleClientException("Certificate could not be read: " + e.getMessage(), e);
|
throw new GuacamoleServerException("Certificate could not be read: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract user's DN from their X.509 certificate
|
// Extract user's DN from their X.509 certificate in LDAP (RFC 4919) format
|
||||||
Principal principal = cert.getSubjectX500Principal();
|
X500Name subject = X500Name.getInstance(RFC4519Style.INSTANCE, cert.getSubject());
|
||||||
return getUsername(principal.getName());
|
return getUsername(subject.toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user