Last active
October 19, 2023 12:38
-
-
Save ShahOdin/c1bd8ea3042bb2ae813c2102082d5325 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package activation.service.instances.amazon | |
import cats.effect.{IO, IOApp, Resource, Sync} | |
import com.nimbusds.jose.util.X509CertUtils | |
import cats.syntax.all.* | |
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo | |
import org.bouncycastle.jce.provider.BouncyCastleProvider | |
import org.bouncycastle.openssl.{PEMParser, PEMKeyPair} | |
import java.security.Signature | |
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter | |
import java.io.StringReader | |
import java.security.cert.X509Certificate | |
import java.security.interfaces.RSAPrivateCrtKey | |
import java.security.{PrivateKey, Security} | |
import scala.util.Try | |
import scala.util.chaining.* | |
import java.security.spec.RSAPublicKeySpec | |
import java.security.KeyFactory | |
import scala.io.Source | |
object TestApp extends IOApp.Simple: | |
def read[F[_] : Sync](resourcePath: String): Resource[F, String] = Resource | |
.fromAutoCloseable[F, Source]( | |
Sync[F].blocking(Source.fromResource(resourcePath)) | |
) | |
.map(_.getLines.mkString(System.lineSeparator())) | |
private def parseRSAPrivateKey(privateKeyPEM: String): IO[PrivateKey] = { | |
new StringReader(privateKeyPEM) | |
.pipe(new PEMParser(_)) | |
.pipe(parser => | |
Try(parser.readObject()).liftTo[IO] | |
.flatMap { | |
case obj if obj.isInstanceOf[PrivateKeyInfo] => obj.asInstanceOf[PrivateKeyInfo].pure | |
case obj if obj.isInstanceOf[PEMKeyPair] => obj.asInstanceOf[PEMKeyPair].getPrivateKeyInfo.pure | |
case _ => IO.raiseError(new RuntimeException("Boom!")) | |
} | |
.flatMap(privateKeyInfo => | |
Try(new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo)).liftTo[IO] | |
) | |
) | |
} | |
private def checkCompatibility(certificate: X509Certificate, privateKey: PrivateKey): IO[Unit] = for | |
_ <- Try(Security.addProvider(new BouncyCastleProvider())).liftTo[IO] | |
publicKey <- privateKey.asInstanceOf[RSAPrivateCrtKey] | |
.pipe(crt => new RSAPublicKeySpec(crt.getModulus, crt.getPublicExponent)) | |
.pipe(rsaPublicKeySpec => | |
Try(KeyFactory.getInstance("RSA")) | |
.flatMap(keyFactory => Try(keyFactory.generatePublic(rsaPublicKeySpec))) | |
.liftTo[IO] | |
) | |
matches = publicKey.equals(certificate.getPublicKey) | |
_ <- IO.println(s"public keys match: $matches") | |
yield () | |
private def roundTrip(certificate: X509Certificate, privateKey: PrivateKey): IO[Unit] = for | |
_ <- Try(Security.addProvider(new BouncyCastleProvider())).liftTo[IO] | |
signatureData <- Try("TEST".getBytes("UTF-8")).liftTo[IO] | |
algorithm = "SHA1withRSA" | |
signer = Signature.getInstance(algorithm,"BC") | |
_ <- Try(signer.initSign(privateKey)).liftTo[IO] | |
_ <- Try(signer.update(signatureData)).liftTo[IO] | |
signature <- Try(signer.sign()).liftTo[IO] | |
_ <- Try(signer.initVerify(certificate)).liftTo[IO] | |
_ <- Try(signer.update(signatureData)).liftTo[IO] | |
verified <- Try(signer.verify(signature)).liftTo[IO] | |
_ <- IO.println(s"roundtrip sign verification: $verified") | |
yield () | |
//how the key and certificate were generated | |
//openssl genpkey -algorithm RSA -out my-rsa.key | |
//openssl req -new -key my-rsa.key -out my-rsa.csr -sha1 | |
//openssl x509 -req -in my-rsa.csr -signkey my-rsa.key -out my-rsa.crt -sha1 | |
//openssl x509 -in my-rsa.crt -out my-rsa.pem -outform PEM | |
//the following all return the same value. ie the key and the certificate do indeed match. | |
//openssl rsa -noout -modulus -in my-rsa.key | openssl md5 | |
//openssl req -noout -modulus -in my-rsa.csr | openssl md5 | |
//openssl x509 -noout -modulus -in my-rsa.crt | openssl md5 | |
override def run: IO[Unit] = ( | |
read[IO]("my-rsa.pem").evalMap(s => IO.delay(X509CertUtils.parse(s))), | |
read[IO]("my-rsa.key").evalMap(parseRSAPrivateKey) | |
).tupled | |
.evalTap(checkCompatibility.tupled) | |
.evalTap(roundTrip.tupled) | |
.use_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment