CryptoUtil.kt (12132B)
1 /* 2 * This file is part of LibEuFin. 3 * Copyright (C) 2024 Taler Systems S.A. 4 5 * LibEuFin is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation; either version 3, or 8 * (at your option) any later version. 9 10 * LibEuFin is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General 13 * Public License for more details. 14 15 * You should have received a copy of the GNU Affero General Public 16 * License along with LibEuFin; see the file COPYING. If not, see 17 * <http://www.gnu.org/licenses/> 18 */ 19 20 package tech.libeufin.common.crypto 21 22 import org.bouncycastle.asn1.x500.X500Name 23 import org.bouncycastle.asn1.x509.BasicConstraints 24 import org.bouncycastle.asn1.x509.Extension 25 import org.bouncycastle.asn1.x509.KeyUsage 26 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter 27 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder 28 import org.bouncycastle.crypto.generators.Argon2BytesGenerator 29 import org.bouncycastle.crypto.generators.BCrypt 30 import org.bouncycastle.crypto.params.Argon2Parameters 31 import org.bouncycastle.jce.provider.BouncyCastleProvider 32 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder 33 import tech.libeufin.common.* 34 import java.io.ByteArrayOutputStream 35 import java.io.InputStream 36 import java.math.BigInteger 37 import java.security.KeyFactory 38 import java.security.KeyPairGenerator 39 import java.security.MessageDigest 40 import java.security.Signature 41 import java.security.cert.CertificateFactory 42 import java.security.cert.X509Certificate 43 import java.security.interfaces.RSAPrivateCrtKey 44 import java.security.interfaces.RSAPublicKey 45 import java.security.spec.* 46 import java.util.* 47 import javax.crypto.* 48 import javax.crypto.spec.IvParameterSpec 49 import javax.crypto.spec.PBEKeySpec 50 import javax.crypto.spec.SecretKeySpec 51 52 /** Helpers for dealing with cryptographic operations in EBICS / LibEuFin */ 53 object CryptoUtil { 54 55 private val provider = BouncyCastleProvider() 56 57 /** Load an RSA private key from its binary PKCS#8 encoding */ 58 fun loadRSAPrivate(encodedPrivateKey: ByteArray): RSAPrivateCrtKey { 59 val spec = PKCS8EncodedKeySpec(encodedPrivateKey) 60 val priv = KeyFactory.getInstance("RSA").generatePrivate(spec) 61 return priv as RSAPrivateCrtKey 62 } 63 64 /** Load an RSA public key from its binary X509 encoding */ 65 fun loadRSAPublic(encodedPublicKey: ByteArray): RSAPublicKey { 66 val spec = X509EncodedKeySpec(encodedPublicKey) 67 val pub = KeyFactory.getInstance("RSA").generatePublic(spec) 68 return pub as RSAPublicKey 69 } 70 71 /** Create an RSA public key from its components: [modulus] and [exponent] */ 72 fun RSAPublicFromComponents(modulus: ByteArray, exponent: ByteArray): RSAPublicKey { 73 val modulusBigInt = BigInteger(1, modulus) 74 val exponentBigInt = BigInteger(1, exponent) 75 val spec = RSAPublicKeySpec(modulusBigInt, exponentBigInt) 76 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 77 } 78 79 /** Extract an RSA public key from a [raw] X.509 certificate */ 80 fun RSAPublicFromCertificate(raw: ByteArray): RSAPublicKey { 81 val certificate = CertificateFactory.getInstance("X.509").generateCertificate(raw.inputStream()) 82 return certificate.publicKey as RSAPublicKey 83 } 84 85 /** Generate an RSA public key from a [private] one */ 86 fun RSAPublicFromPrivate(private: RSAPrivateCrtKey): RSAPublicKey { 87 val spec = RSAPublicKeySpec(private.modulus, private.publicExponent) 88 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 89 } 90 91 /** Generate a self-signed X.509 certificate from an RSA [private] key */ 92 fun X509CertificateFromRSAPrivate(private: RSAPrivateCrtKey, name: String): X509Certificate { 93 val start = Date() 94 val calendar = Calendar.getInstance() 95 calendar.time = start 96 calendar.add(Calendar.YEAR, 1_000) 97 val end = calendar.time 98 99 val name = X500Name("CN=$name") 100 val builder = JcaX509v3CertificateBuilder( 101 name, 102 BigInteger(20, Random()), 103 start, 104 end, 105 name, 106 RSAPublicFromPrivate(private) 107 ) 108 109 110 builder.addExtension(Extension.keyUsage, true, KeyUsage( 111 KeyUsage.digitalSignature 112 or KeyUsage.nonRepudiation 113 or KeyUsage.keyEncipherment 114 or KeyUsage.dataEncipherment 115 or KeyUsage.keyAgreement 116 or KeyUsage.keyCertSign 117 or KeyUsage.cRLSign 118 or KeyUsage.encipherOnly 119 or KeyUsage.decipherOnly 120 )) 121 builder.addExtension(Extension.basicConstraints, true, BasicConstraints(true)) 122 123 val certificate = JcaContentSignerBuilder("SHA256WithRSA").build(private) 124 return JcaX509CertificateConverter() 125 .setProvider(provider) 126 .getCertificate(builder.build(certificate)) 127 128 } 129 130 /** Generate an RSA key pair of [keysize] */ 131 fun genRSAPair(keysize: Int): Pair<RSAPrivateCrtKey, RSAPublicKey> { 132 val gen = KeyPairGenerator.getInstance("RSA") 133 gen.initialize(keysize) 134 val pair = gen.genKeyPair() 135 return Pair(pair.private as RSAPrivateCrtKey, pair.public as RSAPublicKey) 136 } 137 /** Generate an RSA private key of [keysize] */ 138 fun genRSAPrivate(keysize: Int): RSAPrivateCrtKey = genRSAPair(keysize).first 139 /** Generate an RSA public key of [keysize] */ 140 fun genRSAPublic(keysize: Int): RSAPublicKey = genRSAPair(keysize).second 141 142 /** 143 * Hash an RSA public key according to the EBICS standard (EBICS 2.5: 4.4.1.2.3). 144 */ 145 fun getEbicsPublicKeyHash(publicKey: RSAPublicKey): ByteArray { 146 val keyBytes = ByteArrayOutputStream() 147 keyBytes.writeBytes(publicKey.publicExponent.encodeHex().trimStart('0').toByteArray()) 148 keyBytes.write(' '.code) 149 keyBytes.writeBytes(publicKey.modulus.encodeHex().trimStart('0').toByteArray()) 150 val digest = MessageDigest.getInstance("SHA-256") 151 return digest.digest(keyBytes.toByteArray()) 152 } 153 154 fun genEbicsE002Key(encryptionPublicKey: RSAPublicKey): Pair<SecretKey, ByteArray> { 155 // Gen transaction key 156 val keygen = KeyGenerator.getInstance("AES", provider) 157 keygen.init(128) 158 val transactionKey = keygen.generateKey() 159 // Encrypt transaction keyA 160 val cipher = Cipher.getInstance( 161 "RSA/None/PKCS1Padding", 162 provider 163 ) 164 cipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey) 165 val encryptedTransactionKey = cipher.doFinal(transactionKey.encoded) 166 return Pair(transactionKey, encryptedTransactionKey) 167 } 168 169 /** 170 * Encrypt data according to the EBICS E002 encryption process. 171 */ 172 fun encryptEbicsE002( 173 transactionKey: SecretKey, 174 data: InputStream 175 ): CipherInputStream { 176 val cipher = Cipher.getInstance( 177 "AES/CBC/X9.23Padding", 178 provider 179 ) 180 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 181 cipher.init(Cipher.ENCRYPT_MODE, transactionKey, ivParameterSpec) 182 return CipherInputStream(data, cipher) 183 } 184 185 fun decryptEbicsE002Key( 186 privateKey: RSAPrivateCrtKey, 187 encryptedTransactionKey: ByteArray 188 ): SecretKeySpec { 189 val cipher = Cipher.getInstance( 190 "RSA/None/PKCS1Padding", 191 provider 192 ) 193 cipher.init(Cipher.DECRYPT_MODE, privateKey) 194 val transactionKeyBytes = cipher.doFinal(encryptedTransactionKey) 195 return SecretKeySpec(transactionKeyBytes, "AES") 196 } 197 198 fun decryptEbicsE002( 199 transactionKey: SecretKeySpec, 200 encryptedData: InputStream 201 ): CipherInputStream { 202 val cipher = Cipher.getInstance( 203 "AES/CBC/X9.23Padding", 204 provider 205 ) 206 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 207 cipher.init(Cipher.DECRYPT_MODE, transactionKey, ivParameterSpec) 208 return CipherInputStream(encryptedData, cipher) 209 } 210 211 /** 212 * Signing algorithm corresponding to the EBICS A006 signing process. 213 * 214 * Note that while [data] can be arbitrary-length data, in EBICS, the order 215 * data is *always* hashed *before* passing it to the signing algorithm, which again 216 * uses a hash internally. 217 */ 218 fun signEbicsA006(data: ByteArray, privateKey: RSAPrivateCrtKey): ByteArray { 219 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 220 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 221 signature.initSign(privateKey) 222 signature.update(data) 223 return signature.sign() 224 } 225 226 fun verifyEbicsA006(sig: ByteArray, data: ByteArray, publicKey: RSAPublicKey): Boolean { 227 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 228 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 229 signature.initVerify(publicKey) 230 signature.update(data) 231 return signature.verify(sig) 232 } 233 234 fun digestEbicsOrderA006(orderData: ByteArray): ByteArray { 235 val digest = MessageDigest.getInstance("SHA-256") 236 for (b in orderData) { 237 when (b) { 238 '\r'.code.toByte(), '\n'.code.toByte(), (26).toByte() -> Unit 239 else -> digest.update(b) 240 } 241 } 242 return digest.digest() 243 } 244 245 fun decryptKey(data: EncryptedPrivateKeyInfo, passphrase: String): RSAPrivateCrtKey { 246 /* make key out of passphrase */ 247 val pbeKeySpec = PBEKeySpec(passphrase.toCharArray()) 248 val keyFactory = SecretKeyFactory.getInstance(data.algName) 249 val secretKey = keyFactory.generateSecret(pbeKeySpec) 250 /* Make a cipher */ 251 val cipher = Cipher.getInstance(data.algName) 252 cipher.init( 253 Cipher.DECRYPT_MODE, 254 secretKey, 255 data.algParameters // has hash count and salt 256 ) 257 /* Ready to decrypt */ 258 val decryptedKeySpec: PKCS8EncodedKeySpec = data.getKeySpec(cipher) 259 val priv = KeyFactory.getInstance("RSA").generatePrivate(decryptedKeySpec) 260 return priv as RSAPrivateCrtKey 261 } 262 263 fun hashStringSHA256(input: String): ByteArray = 264 MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8)) 265 266 fun bcrypt(password: String, salt: ByteArray, cost: Int): ByteArray { 267 val pwBytes = BCrypt.passwordToByteArray(password.toCharArray()) 268 return BCrypt.generate(pwBytes, salt, cost) 269 } 270 271 fun hashArgon2id(password: String, salt: ByteArray): ByteArray { 272 // OSWAP recommended config https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id 273 val builder = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) 274 .withIterations(1) 275 .withMemoryAsKB(47104) 276 .withParallelism(1) 277 .withSalt(salt) 278 279 val gen = Argon2BytesGenerator() 280 gen.init(builder.build()) 281 282 val result = ByteArray(32) 283 gen.generateBytes(password.toCharArray(), result, 0, result.size) 284 return result 285 } 286 287 private fun mfaBodyHash(body: ByteArray, salt: Base32Crockford16B): Base32Crockford64B { 288 val digest = MessageDigest.getInstance("SHA-512") 289 digest.update(salt.raw) 290 val hash = digest.digest(body) 291 return Base32Crockford64B(hash) 292 } 293 294 fun mfaBodyHashCheck(body: ByteArray, hash: Base32Crockford64B, salt: Base32Crockford16B): Boolean { 295 val check = mfaBodyHash(body, salt) 296 return check == hash 297 } 298 299 fun mfaBodyHashCreate(body: ByteArray): Pair<Base32Crockford64B, Base32Crockford16B> { 300 val salt = Base32Crockford16B.secureRand() 301 val hash = mfaBodyHash(body, salt) 302 return Pair(hash, salt) 303 } 304 }