8148188: Enhance the security libraries to record events of interest

Reviewed-by: egahlin, mullan, weijun, xuelei
This commit is contained in:
Sean Coffey 2018-11-20 13:12:48 +00:00
parent dc260a5369
commit 73ad9c4a00
35 changed files with 2617 additions and 8 deletions

View file

@ -30,6 +30,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.io.*;
import java.net.URL;
import jdk.internal.event.EventHelper;
import jdk.internal.event.SecurityPropertyModificationEvent;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.StaticProperty;
import sun.security.util.Debug;
@ -792,9 +794,19 @@ public final class Security {
* @see java.security.SecurityPermission
*/
public static void setProperty(String key, String datum) {
check("setProperty."+key);
check("setProperty." + key);
props.put(key, datum);
invalidateSMCache(key); /* See below. */
SecurityPropertyModificationEvent spe = new SecurityPropertyModificationEvent();
// following is a no-op if event is disabled
spe.key = key;
spe.value = datum;
spe.commit();
if (EventHelper.isLoggingSecurity()) {
EventHelper.logSecurityPropertyEvent(key, datum);
}
}
/*

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* A helper class to have events logged to a JDK Event Logger.
*/
public final class EventHelper {
private static final System.Logger.Level LOG_LEVEL = System.Logger.Level.DEBUG;
// helper class used for logging security related events for now
private static final String SECURITY_LOGGER_NAME = "jdk.event.security";
private static final System.Logger SECURITY_LOGGER =
System.getLogger(SECURITY_LOGGER_NAME);
private static final boolean LOGGING_SECURITY =
SECURITY_LOGGER.isLoggable(LOG_LEVEL);
public static void logTLSHandshakeEvent(Instant start,
String peerHost,
int peerPort,
String cipherSuite,
String protocolVersion,
long peerCertId) {
String prepend = getDurationString(start);
SECURITY_LOGGER.log(LOG_LEVEL, prepend +
" TLSHandshake: {0}:{1,number,#}, {2}, {3}, {4,number,#}",
peerHost, peerPort, protocolVersion, cipherSuite, peerCertId);
}
public static void logSecurityPropertyEvent(String key,
String value) {
if (isLoggingSecurity()) {
SECURITY_LOGGER.log(LOG_LEVEL,
"SecurityPropertyModification: key:{0}, value:{1}", key, value);
}
}
public static void logX509ValidationEvent(int anchorCertId,
int[] certIds) {
String codes = IntStream.of(certIds)
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));
SECURITY_LOGGER.log(LOG_LEVEL,
"ValidationChain: {0,number,#}, {1}", anchorCertId, codes);
}
public static void logX509CertificateEvent(String algId,
String serialNum,
String subject,
String issuer,
String keyType,
int length,
long certId,
long beginDate,
long endDate) {
SECURITY_LOGGER.log(LOG_LEVEL, "X509Certificate: Alg:{0}, Serial:{1}" +
", Subject:{2}, Issuer:{3}, Key type:{4}, Length:{5,number,#}" +
", Cert Id:{6,number,#}, Valid from:{7}, Valid until:{8}",
algId, serialNum, subject, issuer, keyType, length,
certId, new Date(beginDate), new Date(endDate));
}
/**
* Method to calculate a duration timestamp for events which measure
* the start and end times of certain operations.
* @param start Instant indicating when event started recording
* @return A string representing duraction from start time to
* time of this method call. Empty string is start is null.
*/
private static String getDurationString(Instant start) {
if (start != null) {
Duration duration = Duration.between(start, Instant.now());
long micros = duration.toNanos() / 1_000;
if (micros < 1_000_000) {
return "duration = " + (micros / 1_000.0) + " ms:";
} else {
return "duration = " + ((micros / 1_000) / 1_000.0) + " s:";
}
} else {
return "";
}
}
/**
* Helper to determine if security events are being logged
* at a preconfigured logging level. The configuration value
* is read once at class initialization.
*
* @return boolean indicating whether an event should be logged
*/
public static boolean isLoggingSecurity() {
return LOGGING_SECURITY;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
/**
* Event details relating to the modification of a Security property.
*/
public final class SecurityPropertyModificationEvent extends Event {
public String key;
public String value;
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
/**
* Event recording details of successful TLS handshakes.
*/
public final class TLSHandshakeEvent extends Event {
public String peerHost;
public int peerPort;
public String protocolVersion;
public String cipherSuite;
public long certificateId;
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
/**
* Event recording details of X.509 Certificate.
*/
public final class X509CertificateEvent extends Event {
public String algorithm;
public String serialNumber;
public String subject;
public String issuer;
public String keyType;
public int keyLength;
public long certificateId;
public long validFrom;
public long validUntil;
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
/**
* Event recording details of X.509 Certificate serial numbers
* used in X509 cert path validation.
*/
public final class X509ValidationEvent extends Event {
public long certificateId;
public int certificatePosition;
public long validationCounter;
}

View file

@ -26,12 +26,15 @@
package sun.security.provider;
import java.io.*;
import java.security.PublicKey;
import java.util.*;
import java.security.cert.*;
import jdk.internal.event.EventHelper;
import jdk.internal.event.X509CertificateEvent;
import sun.security.util.KeyUtil;
import sun.security.util.Pem;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CRLImpl;
import sun.security.x509.*;
import sun.security.pkcs.PKCS7;
import sun.security.provider.certpath.X509CertPath;
import sun.security.provider.certpath.X509CertificatePair;
@ -101,6 +104,8 @@ public class X509Factory extends CertificateFactorySpi {
}
cert = new X509CertImpl(encoding);
addToCache(certCache, cert.getEncodedInternal(), cert);
// record cert details if necessary
commitEvent(cert);
return cert;
} else {
throw new IOException("Empty input");
@ -762,4 +767,43 @@ public class X509Factory extends CertificateFactorySpi {
}
return tag;
}
private void commitEvent(X509CertImpl info) {
X509CertificateEvent xce = new X509CertificateEvent();
if (xce.shouldCommit() || EventHelper.isLoggingSecurity()) {
PublicKey pKey = info.getPublicKey();
String algId = info.getSigAlgName();
String serNum = info.getSerialNumber().toString(16);
String subject = info.getSubjectDN().getName();
String issuer = info.getIssuerDN().getName();
String keyType = pKey.getAlgorithm();
int length = KeyUtil.getKeySize(pKey);
int hashCode = info.hashCode();
long beginDate = info.getNotBefore().getTime();
long endDate = info.getNotAfter().getTime();
if (xce.shouldCommit()) {
xce.algorithm = algId;
xce.serialNumber = serNum;
xce.subject = subject;
xce.issuer = issuer;
xce.keyType = keyType;
xce.keyLength = length;
xce.certificateId = hashCode;
xce.validFrom = beginDate;
xce.validUntil = endDate;
xce.commit();
}
if (EventHelper.isLoggingSecurity()) {
EventHelper.logX509CertificateEvent(algId,
serNum,
subject,
issuer,
keyType,
length,
hashCode,
beginDate,
endDate);
}
}
}
}

View file

@ -29,7 +29,10 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import jdk.internal.event.X509ValidationEvent;
import jdk.internal.event.EventHelper;
import sun.security.provider.certpath.PKIX.ValidatorParams;
import sun.security.validator.Validator;
import sun.security.x509.X509CertImpl;
@ -47,6 +50,7 @@ import sun.security.util.Debug;
public final class PKIXCertPathValidator extends CertPathValidatorSpi {
private static final Debug debug = Debug.getInstance("certpath");
private static final AtomicLong validationCounter = new AtomicLong();
/**
* Default constructor.
@ -234,7 +238,33 @@ public final class PKIXCertPathValidator extends CertPathValidatorSpi {
params.certificates(),
certPathCheckers);
X509ValidationEvent xve = new X509ValidationEvent();
if (xve.shouldCommit() || EventHelper.isLoggingSecurity()) {
int[] certIds = params.certificates().stream()
.mapToInt(x -> x.hashCode())
.toArray();
int anchorCertId =
anchor.getTrustedCert().hashCode();
if (xve.shouldCommit()) {
xve.certificateId = anchorCertId;
int certificatePos = 1; //anchor cert
xve.certificatePosition = certificatePos;
xve.validationCounter = validationCounter.incrementAndGet();
xve.commit();
// now, iterate through remaining
for (int id : certIds) {
xve.certificateId = id;
xve.certificatePosition = ++certificatePos;
xve.commit();
}
}
if (EventHelper.isLoggingSecurity()) {
EventHelper.logX509ValidationEvent(anchorCertId, certIds);
}
}
return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
bc.getPublicKey());
}
}

View file

@ -40,6 +40,10 @@ import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLPeerUnverifiedException;
import jdk.internal.event.EventHelper;
import jdk.internal.event.TLSHandshakeEvent;
import sun.security.internal.spec.TlsPrfParameterSpec;
import sun.security.ssl.CipherSuite.HashAlg;
import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
@ -548,6 +552,7 @@ final class Finished {
// handshake context cleanup.
chc.handshakeFinished = true;
recordEvent(chc.conContext.conSession);
// May need to retransmit the last flight for DTLS.
if (!chc.sslContext.isDTLS()) {
@ -597,6 +602,7 @@ final class Finished {
// handshake context cleanup.
shc.handshakeFinished = true;
recordEvent(shc.conContext.conSession);
// May need to retransmit the last flight for DTLS.
if (!shc.sslContext.isDTLS()) {
@ -730,6 +736,8 @@ final class Finished {
// handshake context cleanup.
chc.handshakeFinished = true;
chc.conContext.finishHandshake();
recordEvent(chc.conContext.conSession);
// The handshake message has been delivered.
return null;
@ -1063,6 +1071,7 @@ final class Finished {
if (!shc.sslContext.isDTLS()) {
shc.conContext.finishHandshake();
}
recordEvent(shc.conContext.conSession);
//
// produce
@ -1074,4 +1083,35 @@ final class Finished {
}
}
private static void recordEvent(SSLSessionImpl session) {
TLSHandshakeEvent event = new TLSHandshakeEvent();
if (event.shouldCommit() || EventHelper.isLoggingSecurity()) {
int peerCertificateId = 0;
try {
// use hash code for Id
peerCertificateId = session
.getCertificateChain()[0]
.hashCode();
} catch (SSLPeerUnverifiedException e) {
// not verified msg
}
if (event.shouldCommit()) {
event.peerHost = session.getPeerHost();
event.peerPort = session.getPeerPort();
event.cipherSuite = session.getCipherSuite();
event.protocolVersion = session.getProtocol();
event.certificateId = peerCertificateId;
event.commit();
}
if (EventHelper.isLoggingSecurity()) {
EventHelper.logTLSHandshakeEvent(null,
session.getPeerHost(),
session.getPeerPort(),
session.getCipherSuite(),
session.getProtocol(),
peerCertificateId);
}
}
}
}