diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenExtractorTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenExtractorTests.java index de9604db5..96f6b1bdd 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenExtractorTests.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenExtractorTests.java @@ -14,15 +14,17 @@ import com.microsoft.bot.connector.authentication.OpenIdMetadataKey; import com.microsoft.bot.connector.authentication.TokenValidationParameters; import java.io.IOException; -import java.math.BigInteger; +import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyPair; -import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -34,20 +36,21 @@ import java.util.concurrent.CompletionException; import org.junit.Before; import org.junit.Test; -import sun.security.x509.AlgorithmId; -import sun.security.x509.CertificateAlgorithmId; -import sun.security.x509.CertificateSerialNumber; -import sun.security.x509.CertificateValidity; -import sun.security.x509.CertificateVersion; -import sun.security.x509.CertificateX509Key; -import sun.security.x509.X500Name; -import sun.security.x509.X509CertImpl; -import sun.security.x509.X509CertInfo; +/** + * Test Notes: + * + * The PKCS12 certificates were created using these steps: + * https://kb.globalscape.com/Knowledgebase/11039/Generating-a-PKCS12-Private-Key-and-Public-Certificate + * + * For the expired cert, just specify a negative number of days in step #4. + * + * For both valid and expired certs, these unit tests expect the alias for both to be "bot-connector-pkcs12" + * and the password to be "botframework" + */ public class JwtTokenExtractorTests { - private X509Certificate validCertificate; - private X509Certificate expiredCertificate; - private KeyPair keyPair; + private CertInfo valid; + private CertInfo expired; @Before public void setup() throws GeneralSecurityException, IOException { @@ -55,25 +58,15 @@ public void setup() throws GeneralSecurityException, IOException { EmulatorValidation.TOKENVALIDATIONPARAMETERS.validateLifetime = false; GovernmentChannelValidation.TOKENVALIDATIONPARAMETERS.validateLifetime = false; - // create keys - keyPair = createKeyPair(); - Date now = new Date(); - Date from = new Date(now.getTime() - (10 * 86400000L)); - - // create expired certificate - Date to = new Date(now.getTime() - (9 * 86400000L)); - expiredCertificate = createSelfSignedCertificate(keyPair, from, to); - - // create valid certificate - to = new Date(now.getTime() + (9 * 86400000L)); - validCertificate = createSelfSignedCertificate(keyPair, from, to); + valid = loadCert("bot-connector.pkcs12"); + expired = loadCert("bot-connector-expired.pkcs12"); } @Test(expected = CompletionException.class) public void JwtTokenExtractor_WithExpiredCert_ShouldNotAllowCertSigningKey() { // this should throw a CompletionException (which contains an AuthenticationException) buildExtractorAndValidateToken( - expiredCertificate, keyPair.getPrivate() + expired.cert, expired.keypair.getPrivate() ).join(); } @@ -81,7 +74,7 @@ public void JwtTokenExtractor_WithExpiredCert_ShouldNotAllowCertSigningKey() { public void JwtTokenExtractor_WithValidCert_ShouldAllowCertSigningKey() { // this should not throw buildExtractorAndValidateToken( - validCertificate, keyPair.getPrivate() + valid.cert, valid.keypair.getPrivate() ).join(); } @@ -92,7 +85,7 @@ public void JwtTokenExtractor_WithExpiredToken_ShouldNotAllow() { Date issuedAt = new Date(now.getTime() - 86400000L); buildExtractorAndValidateToken( - expiredCertificate, keyPair.getPrivate(), issuedAt + expired.cert, expired.keypair.getPrivate(), issuedAt ).join(); } @@ -161,45 +154,24 @@ private static TokenValidationParameters createTokenValidationParameters(X509Cer }}; } - private KeyPair createKeyPair() throws NoSuchAlgorithmException { - // note that this isn't allowing for a "kid" value - KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); - generator.initialize(2048); - return generator.generateKeyPair(); + private static class CertInfo { + public X509Certificate cert; + public KeyPair keypair; } - private static X509Certificate createSelfSignedCertificate( - KeyPair pair, Date from, Date to - ) throws GeneralSecurityException, IOException { - String dn = "CN=Bot, OU=BotFramework, O=Microsoft, C=US"; - String algorithm = "SHA256withRSA"; - - PrivateKey privateKey = pair.getPrivate(); - X509CertInfo info = new X509CertInfo(); - - CertificateValidity interval = new CertificateValidity(from, to); - BigInteger sn = new BigInteger(64, new SecureRandom()); - X500Name owner = new X500Name(dn); - - info.set(X509CertInfo.VALIDITY, interval); - info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); - info.set(X509CertInfo.SUBJECT, owner); - info.set(X509CertInfo.ISSUER, owner); - info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); - info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); - AlgorithmId algo = new AlgorithmId(AlgorithmId.sha256WithRSAEncryption_oid); - info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); - - // Sign the cert to identify the algorithm that's used. - X509CertImpl cert = new X509CertImpl(info); - cert.sign(privateKey, algorithm); - - // Update the algorithm, and resign. - algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG); - info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); - cert = new X509CertImpl(info); - cert.sign(privateKey, algorithm); - return cert; + private static CertInfo loadCert(String pkcs12File) + throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, + UnrecoverableKeyException { + InputStream fis = ClassLoader.getSystemResourceAsStream(pkcs12File); + KeyStore p12 = KeyStore.getInstance("pkcs12"); + p12.load(fis, "botframework".toCharArray()); + + return new CertInfo() {{ + cert = (X509Certificate) p12.getCertificate("bot-connector-pkcs12"); + keypair = new KeyPair(cert.getPublicKey(), + (PrivateKey) p12.getKey("bot-connector-pkcs12", "botframework".toCharArray()) + ); + }}; } private static String encodeCertificate(Certificate certificate) { diff --git a/libraries/bot-connector/src/test/resources/bot-connector-expired.pkcs12 b/libraries/bot-connector/src/test/resources/bot-connector-expired.pkcs12 new file mode 100644 index 000000000..ac9470061 Binary files /dev/null and b/libraries/bot-connector/src/test/resources/bot-connector-expired.pkcs12 differ diff --git a/libraries/bot-connector/src/test/resources/bot-connector.key b/libraries/bot-connector/src/test/resources/bot-connector.key new file mode 100644 index 000000000..e5e43c892 --- /dev/null +++ b/libraries/bot-connector/src/test/resources/bot-connector.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAy8VcIYWh4sF2XcZF7u+lSq7YdE8skZhmtDF5w8GoEoPscGyT +wSeWjK7u+y2g5iAZsXQwzhN1VvOHznAlLrswLaORie7ch9veFeTMT60gLL2w8e6h +RUWxNJiQXjeExwk8Fhvcq7Kpl+qM4iEHvDX6iXCjEkNJ4Ghx48j9siUKMf1T8IcZ +sE3zzaaGOU5ar8NVsf9Kz1kPCOYv8zqykB45nUDsbE/q9cEkumL8ebjd1JApDJf0 +sf/PkftMvwP69QY1CJ/achUsRqDvFGLw+ZyUyXaSdPt92H92vbNzo8hn6GuxnPHy +f/Aoxjy/o5zNocsiiJvgFBFjRBZXWhk3OJWKZwIDAQABAoIBADNiKx9Q4Uea3Uw8 +STo9OAMjH/YEWQrF0XAy4a+ZT9aLab3Xw1J7txz2p9Cy6tXc1l3HHN96TKaGdoJ6 +CQZFsZpwmqybjQS9Tr1amqKk124wz0PSltwu/MZ0ikMX4OWH0J0KnZS2Usm6HZiQ +F7FAM1MhEh3y1dg+vilgb4jSikWcVp7RbcMgwG0N9oQhbPqN+Bv/E5KBpYk1Rdwt +yjSBOdDenx+TF5RrdKYxC3ouKN0BJMgtIIkv+iGce1WMtSAHkNXrgxJ9AhDquREU +Op363fad01Ulvs4Z+IDlEhrpA6oUt5hr8lStFhrhVuDPqctAWz27+YNw5ioHbimA +U48EOzECgYEA7T5dQzKXIzrDha4Spr8KbCMvXsesehWG3RLZxhU7lnjSMAhBuKGU +hZMfjmSSBR/rr7ppMjN4LLmpjqTkhojg/p+MnK9XRSvmGaSxlS1K8hANjShhx6s7 +xWbEsKQEPbuq1m0vCAzxgGkxLpRmM9ajJhFYEoSdVbc/hhVI72mxhiUCgYEA2+GI +ig0Wpbq5D1ISwljWHhpdi4d0MeO+wtogMo46AAX4kpgCEKDmPwX2igJKJjQJyWNB +PqHXPPQJyferun27baiNA5vEDzoCRvyLqKjyLrzSS+wDszR4mYhK8naILPonmIca +9BsUDcQw7x3rzVUlOKFpbRfT0qPVa5qn3T32apsCgYEAmYJvCloj3ZHajhdSzj5z +WgFyV1vQSLbBKy9VZoy6n+TR7G6LSBKVbdEC7Do7GcHL2Us/YlJXgmkoQ7qCfGL5 +YwiODZyPVZzQKOueVK6X/gVRH3NvwakU5ehXgQzACcnzAwhnFEh7w+FNB5zSfNx3 +eNxkJqdUvu/x1KrVJMU5L1kCgYEAkIokYGOUNKOXDTwterY9IpLAVX1YY4dLmfkb +W0BlXiiOq4bjLJ0oXduEolo49f4VRN5LQGnQ/I+Lc8msiK4oLEC1Wd7mNgAzCQjw +oZFVimWzdBcUo5Plhz+xzMsgXzieGMUPcdHvD9GdPUKVBGhpTF3G2ODl7LyoCdEj +cetOdesCgYAIuxFR/89S44Je5maTMkcExZpVTm1D1Zc8EmlHQ+WPjrakZSWFx2TS +o8wUd/mCwCTLRG3S2t3eUZiEi+G9gI8bE/w7ABxNCFAlHbo0SETy7T+9XeznoFbZ +0FyvVLvXQVZhKPVTF0pYkfuHo3ofbotKbTEM62EurroU1dviRJ7Seg== +-----END RSA PRIVATE KEY----- diff --git a/libraries/bot-connector/src/test/resources/bot-connector.pkcs12 b/libraries/bot-connector/src/test/resources/bot-connector.pkcs12 new file mode 100644 index 000000000..e9854a381 Binary files /dev/null and b/libraries/bot-connector/src/test/resources/bot-connector.pkcs12 differ