package play.core.server.ssl
import play.api.Logger
import java.security.{ KeyStore, SecureRandom, KeyPairGenerator, KeyPair }
import sun.security.x509._
import java.util.Date
import java.math.BigInteger
import java.security.cert.X509Certificate
import java.io.{ File, FileInputStream, FileOutputStream }
import javax.net.ssl.KeyManagerFactory
import scala.util.Properties.isJavaAtLeast
import play.utils.PlayIO
import java.security.interfaces.RSAPublicKey
object FakeKeyStore {
private val logger = Logger(FakeKeyStore.getClass)
val GeneratedKeyStore = "conf/generated.keystore"
val DnName = "CN=localhost, OU=Unit Testing, O=Mavericks, L=Moon Base 1, ST=Cyberspace, C=CY"
def shouldGenerate(keyStoreFile: File): Boolean = {
import scala.collection.JavaConverters._
if (!keyStoreFile.exists()) {
return true
}
val store = KeyStore.getInstance("JKS")
val in = new FileInputStream(keyStoreFile)
try {
store.load(in, "".toCharArray)
} finally {
PlayIO.closeQuietly(in)
}
store.aliases().asScala.foreach {
alias =>
Option(store.getCertificate(alias)).map {
c =>
val key: RSAPublicKey = c.getPublicKey.asInstanceOf[RSAPublicKey]
if (key.getModulus.bitLength < 2048) {
return true
}
}
}
false
}
def keyManagerFactory(appPath: File): KeyManagerFactory = {
val keyStore = KeyStore.getInstance("JKS")
val keyStoreFile = new File(appPath, GeneratedKeyStore)
if (shouldGenerate(keyStoreFile)) {
logger.info("Generating HTTPS key pair in " + keyStoreFile.getAbsolutePath + " - this may take some time. If nothing happens, try moving the mouse/typing on the keyboard to generate some entropy.")
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
keyPairGenerator.initialize(2048)
val keyPair = keyPairGenerator.generateKeyPair()
val cert = createSelfSignedCertificate(keyPair)
keyStore.load(null, "".toCharArray)
keyStore.setKeyEntry("playgenerated", keyPair.getPrivate, "".toCharArray, Array(cert))
keyStore.setCertificateEntry("playgeneratedtrusted", cert)
val out = new FileOutputStream(keyStoreFile)
try {
keyStore.store(out, "".toCharArray)
} finally {
PlayIO.closeQuietly(out)
}
} else {
val in = new FileInputStream(keyStoreFile)
try {
keyStore.load(in, "".toCharArray)
} finally {
PlayIO.closeQuietly(in)
}
}
val kmf = KeyManagerFactory.getInstance("SunX509")
kmf.init(keyStore, "".toCharArray)
kmf
}
def createSelfSignedCertificate(keyPair: KeyPair): X509Certificate = {
val certInfo = new X509CertInfo()
certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new BigInteger(64, new SecureRandom())))
certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3))
val validFrom = new Date()
val validTo = new Date(validFrom.getTime + 50l * 365l * 24l * 60l * 60l * 1000l)
val validity = new CertificateValidity(validFrom, validTo)
certInfo.set(X509CertInfo.VALIDITY, validity)
val owner = new X500Name(DnName)
val justName = isJavaAtLeast("1.8")
certInfo.set(X509CertInfo.SUBJECT, if (justName) owner else new CertificateSubjectName(owner))
certInfo.set(X509CertInfo.ISSUER, if (justName) owner else new CertificateIssuerName(owner))
certInfo.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic))
val algorithm = new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)
certInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithm))
val cert = new X509CertImpl(certInfo)
cert.sign(keyPair.getPrivate, "SHA1withRSA")
val actualAlgorithm = cert.get(X509CertImpl.SIG_ALG).asInstanceOf[AlgorithmId]
certInfo.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, actualAlgorithm)
val newCert = new X509CertImpl(certInfo)
newCert.sign(keyPair.getPrivate, "SHA1withRSA")
newCert
}
}