CryptoUtils.java
package io.mersel.dss.signer.api.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.PrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Locale;
/**
* Kriptografik ve encoding işlemleri için yardımcı metodlar.
*/
public final class CryptoUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(CryptoUtils.class);
private CryptoUtils() {
// Utility class - instantiation engellendi
}
/**
* Byte array'i hexadecimal string'e çevirir.
*
* @param bytes Çevrilecek byte array
* @return Hexadecimal string
*/
public static String bytesToHex(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
/**
* Hexadecimal string'i byte array'e çevirir.
*
* @param hex Hexadecimal string
* @return Byte array
* @throws IllegalArgumentException Geçersiz hex string için
*/
public static byte[] hexToBytes(String hex) {
if (hex == null || hex.isEmpty()) {
return new byte[0];
}
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException("Hex string uzunluğu çift sayı olmalı");
}
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int digit1 = Character.digit(hex.charAt(i), 16);
int digit2 = Character.digit(hex.charAt(i + 1), 16);
if (digit1 == -1 || digit2 == -1) {
throw new IllegalArgumentException("Geçersiz hexadecimal karakter: " + hex.substring(i, i + 2));
}
data[i / 2] = (byte) ((digit1 << 4) + digit2);
}
return data;
}
/**
* Private key tipine göre uygun signature algoritmasını döndürür.
* RSA ve EC (Elliptic Curve) key'leri destekler.
* EC key boyutuna göre SHA256/SHA384/SHA512 otomatik seçilir.
*
* @param privateKey İmzalama için kullanılacak private key
* @return Signature algoritması (örn: "SHA256withRSA", "SHA384withECDSA")
*/
public static String getSignatureAlgorithm(PrivateKey privateKey) {
if (privateKey instanceof RSAPrivateKey) {
LOGGER.debug("RSA private key algılandı, SHA256withRSA kullanılacak");
return "SHA256withRSA";
} else if (privateKey instanceof ECPrivateKey) {
String ecAlgorithm = resolveECSignatureAlgorithm((ECPrivateKey) privateKey);
LOGGER.debug("EC private key algılandı, {} kullanılacak", ecAlgorithm);
return ecAlgorithm;
} else {
String algorithm = privateKey.getAlgorithm();
LOGGER.warn("Bilinmeyen private key tipi: {}, algorithm: {}",
privateKey.getClass().getName(), algorithm);
if ("EC".equalsIgnoreCase(algorithm) || "ECDSA".equalsIgnoreCase(algorithm)) {
return "SHA256withECDSA";
} else if ("RSA".equalsIgnoreCase(algorithm)) {
return "SHA256withRSA";
}
LOGGER.warn("Desteklenmeyen key tipi, SHA256withRSA kullanılacak");
return "SHA256withRSA";
}
}
/**
* Sertifika ve key tipine göre uygun signature algoritmasını döndürür.
* Sertifikanın kendi sigAlgName bilgisini kullanarak doğru hash boyutunu belirler.
*/
public static String getSignatureAlgorithm(PrivateKey privateKey,
java.security.cert.X509Certificate certificate) {
if (certificate != null) {
java.security.PublicKey publicKey = certificate.getPublicKey();
String certSigAlg = certificate.getSigAlgName();
if (publicKey instanceof RSAPublicKey) {
// Açık anahtar RSA ise, sertifika algoritmasından SHA kısmını al ve RSA ile birleştir
if (certSigAlg != null && !certSigAlg.isEmpty()) {
String shaPrefix = extractShaPrefix(certSigAlg);
String result = shaPrefix + "withRSA";
LOGGER.debug("Sertifika RSA açık anahtara sahip, algoritma düzeltildi: {} -> {}", certSigAlg, result);
return result;
}
return "SHA256withRSA";
} else if (publicKey instanceof ECPublicKey) {
if (certSigAlg != null && !certSigAlg.isEmpty()) {
LOGGER.debug("Sertifika EC açık anahtara sahip, sertifika algoritması kullanılacak: {}", certSigAlg);
return certSigAlg;
}
}
}
return getSignatureAlgorithm(privateKey);
}
private static String extractShaPrefix(String sigAlgorithm) {
String upper = sigAlgorithm.toUpperCase(Locale.ENGLISH);
if (upper.startsWith("SHA")) {
int idx = upper.indexOf("WITH");
if (idx > 0) {
return upper.substring(0, idx);
}
}
return "SHA256";
}
/**
* EC key boyutuna göre uygun hash algoritmasını seçer.
* P-256 -> SHA256, P-384 -> SHA384, P-521 -> SHA512
*/
private static String resolveECSignatureAlgorithm(ECPrivateKey ecKey) {
try {
java.security.spec.ECParameterSpec params = ecKey.getParams();
if (params != null) {
int fieldSize = params.getOrder().bitLength();
if (fieldSize <= 256) {
return "SHA256withECDSA";
} else if (fieldSize <= 384) {
return "SHA384withECDSA";
} else {
return "SHA512withECDSA";
}
}
} catch (Exception e) {
LOGGER.debug("EC parametre okunamadı, SHA256withECDSA kullanılacak", e);
}
return "SHA256withECDSA";
}
}