A Daily - Yash-777/MyWorld GitHub Wiki
import javax.net.ssl.; import java.io.; import java.net.; import java.nio.charset.StandardCharsets; import java.nio.file.; import java.security.; import java.security.cert.; import java.security.cert.Certificate; import java.util.*; import java.util.Base64;
/**
- SslFetchTest β Standalone Java 8 program
- Reproduces and fixes the PKIX error when fetching from:
-
- https://azuredownloads-g3ahgwb5b8bkbxhd.b01.azurefd.net (Eclipse Copilot plugin)
- Run WITHOUT any special JVM flags or keytool steps.
- Compile: javac SslFetchTest.java
- Run: java SslFetchTest
- What it does:
-
- Loads JVM cacerts as base truststore (preserves all built-in CAs)
-
- For each target URL, opens a trust-all socket to capture the live cert chain
-
- Imports every cert into the in-memory truststore (NEW / SKIP / UPDATE)
-
- Installs the patched truststore as the JVM-wide SSLContext default
-
- Fetches each URL using HttpsURLConnection and prints status + first 500 chars
-
- Exports .cer files to exportedcerts/ for inspection */ public class SslFetchTest {
// ββ Target URLs ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
private static final String[] TARGET_URLS = {
"https://azuredownloads-g3ahgwb5b8bkbxhd.b01.azurefd.net/github-copilot/content.xml",
"https://marketplace.eclipse.org/content/github-copilot",
"https://docs.github.com/en/copilot/how-tos/get-code-suggestions/get-ide-code-suggestions"
};
private static final String EXPORT_DIR = "exportedcerts";
private static final int CONNECT_TIMEOUT = 15_000; // ms
private static final int READ_TIMEOUT = 15_000; // ms
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
public static void main(String[] args) throws Exception {
printBanner();
// Step 1 β create export directory
new File(EXPORT_DIR).mkdirs();
// Step 2 β load JVM cacerts as the starting truststore
KeyStore trustStore = loadJvmCacerts();
// Step 3 β for every target URL, fetch and import its cert chain
boolean anyModified = false;
for (String url : TARGET_URLS) {
String host = extractHost(url);
int port = 443;
System.out.println("\nββ Processing: " + host + ":" + port);
List<X509Certificate> chain = fetchCertChain(host, port);
if (chain.isEmpty()) {
System.out.println(" [WARN] No certs fetched β host unreachable or refused");
} else {
boolean modified = syncToTrustStore(trustStore, host, chain);
if (modified) anyModified = true;
}
}
// Step 4 β install patched truststore as JVM-wide default
if (anyModified) {
installAsJvmDefault(trustStore);
persistJks(trustStore);
} else {
System.out.println("\n[INFO] All certs already up-to-date β re-installing anyway to be safe");
installAsJvmDefault(trustStore);
}
// Step 5 β now actually fetch each URL with the patched truststore
System.out.println("\n" + repeat("β", 60));
System.out.println(" FETCHING URLS");
System.out.println(repeat("β", 60));
for (String url : TARGET_URLS) {
fetchAndPrint(url);
}
System.out.println("\n[INFO] Done. Exported certs: " + new File(EXPORT_DIR).getAbsolutePath());
System.out.println("[INFO] Inspect JKS: keytool -list -keystore "
+ EXPORT_DIR + "/combined-truststore.jks -storepass changeit");
}
// =========================================================================
// Step A β Load JVM cacerts
// =========================================================================
/**
* Loads $JAVA_HOME/lib/security/cacerts (Java 11+) or
* $JAVA_HOME/jre/lib/security/cacerts (Java 8) as the base KeyStore.
* Password is always "changeit".
*/
private static KeyStore loadJvmCacerts() throws Exception {
String javaHome = System.getProperty("java.home");
File cacerts = new File(javaHome, "lib/security/cacerts");
if (!cacerts.exists()) {
cacerts = new File(javaHome, "jre/lib/security/cacerts");
}
if (!cacerts.exists()) {
throw new FileNotFoundException("Cannot find cacerts at: " + javaHome);
}
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream fis = new FileInputStream(cacerts)) {
ks.load(fis, "changeit".toCharArray());
}
System.out.println("[INFO] Loaded JVM cacerts: " + cacerts.getAbsolutePath()
+ " (" + ks.size() + " entries)");
return ks;
}
// =========================================================================
// Step B β Capture cert chain via trust-all socket
// =========================================================================
/**
* Opens a temporary TLS socket using a no-validation TrustManager so we can
* capture the certificate chain regardless of whether it's currently trusted.
* The socket is closed immediately after the handshake β only the chain is kept.
*
* SECURITY NOTE: This trust-all approach is used ONLY here during bootstrap
* to read the chain. Once imported, all real connections use proper validation.
*/
private static List<X509Certificate> fetchCertChain(String host, int port) {
List<X509Certificate> chain = new ArrayList<X509Certificate>();
try {
// Capture array β lambdas not available in Java 8, use array trick
final X509Certificate[][] captured = new X509Certificate[1][];
SSLContext trustAll = SSLContext.getInstance("TLS");
trustAll.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] c, String a) {}
public void checkServerTrusted(X509Certificate[] c, String a) { captured[0] = c; }
public X509Certificate[] getAcceptedIssuers() {
return captured[0] != null ? captured[0] : new X509Certificate[0];
}
}}, new SecureRandom());
SSLSocket socket = (SSLSocket) trustAll.getSocketFactory().createSocket(host, port);
try {
socket.setSoTimeout(CONNECT_TIMEOUT);
socket.startHandshake();
for (Certificate c : socket.getSession().getPeerCertificates()) {
if (c instanceof X509Certificate) chain.add((X509Certificate) c);
}
} finally {
try { socket.close(); } catch (Exception ignored) {}
}
System.out.println(" [INFO] Fetched " + chain.size() + " cert(s) from " + host + ":" + port);
for (int i = 0; i < chain.size(); i++) {
X509Certificate c = chain.get(i);
System.out.println(" cert[" + i + "] Subject : " + c.getSubjectDN().getName());
System.out.println(" Issuer : " + c.getIssuerDN().getName());
System.out.println(" Expires : " + c.getNotAfter());
}
} catch (Exception e) {
System.out.println(" [ERROR] Cert fetch failed: " + e.getMessage());
}
return chain;
}
// =========================================================================
// Step C β NEW / SKIP / UPDATE sync
// =========================================================================
private static boolean syncToTrustStore(KeyStore ks, String host,
List<X509Certificate> chain) {
boolean modified = false;
for (int i = 0; i < chain.size(); i++) {
X509Certificate cert = chain.get(i);
String fileName = host + "-cert-" + i + ".cer";
File file = new File(EXPORT_DIR, fileName);
String alias = host.replace(".", "-") + "-" + i;
try {
byte[] liveBytes = cert.getEncoded();
if (!file.exists()) {
System.out.println(" [NEW] " + fileName);
writeCertFile(file, liveBytes);
addAlias(ks, alias, cert);
modified = true;
} else if (file.length() != liveBytes.length) {
System.out.println(" [UPDATE] " + fileName
+ " (disk=" + file.length() + "B live=" + liveBytes.length + "B)");
writeCertFile(file, liveBytes);
removeAlias(ks, alias);
addAlias(ks, alias, cert);
modified = true;
} else {
System.out.println(" [SKIP] " + fileName + " (" + file.length() + "B β unchanged)");
}
} catch (Exception e) {
System.out.println(" [ERROR] cert[" + i + "] " + host + ": " + e.getMessage());
}
}
return modified;
}
// =========================================================================
// Step D β Install patched truststore as JVM default
// =========================================================================
private static void installAsJvmDefault(KeyStore ts) throws Exception {
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), new SecureRandom());
SSLContext.setDefault(ctx);
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
// Also set HostnameVerifier to standard (not trust-all)
HttpsURLConnection.setDefaultHostnameVerifier(new DefaultHostnameVerifier());
System.out.println("\n[INFO] JVM SSLContext patched (" + ts.size() + " trust entries)");
}
// =========================================================================
// Step E β Fetch URL and print result
// =========================================================================
private static void fetchAndPrint(String urlStr) {
System.out.println("\n URL : " + urlStr);
System.out.println(" " + repeat("β", 56));
try {
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent",
"Mozilla/5.0 (SslFetchTest/1.0; Java/" + System.getProperty("java.version") + ")");
conn.setRequestProperty("Accept", "*/*");
int status = conn.getResponseCode();
System.out.println(" HTTP Status : " + status + " " + conn.getResponseMessage());
System.out.println(" Content-Type: " + conn.getContentType());
// Read body (use error stream on non-2xx)
InputStream is = (status >= 200 && status < 300)
? conn.getInputStream() : conn.getErrorStream();
if (is != null) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
String line;
int chars = 0;
while ((line = reader.readLine()) != null && chars < 800) {
sb.append(line).append("\n");
chars += line.length();
}
reader.close();
System.out.println(" Body (first 800 chars):");
System.out.println(" β" + repeat("β", 55));
for (String l : sb.toString().split("\n")) {
System.out.println(" β " + l);
}
System.out.println(" β" + repeat("β", 55));
}
conn.disconnect();
} catch (SSLHandshakeException e) {
System.out.println(" [FAIL] SSLHandshakeException β PKIX still failing: " + e.getMessage());
System.out.println(" This means the ZScaler/proxy cert was not captured.");
System.out.println(" Check exportedcerts/ and run: keytool -list -keystore "
+ EXPORT_DIR + "/combined-truststore.jks -storepass changeit");
} catch (Exception e) {
System.out.println(" [FAIL] " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
// =========================================================================
// Helpers
// =========================================================================
private static String extractHost(String url) {
// Strip scheme and path: https://host.example.com/path β host.example.com
String s = url;
if (s.startsWith("https://")) s = s.substring(8);
if (s.startsWith("http://")) s = s.substring(7);
int slash = s.indexOf('/');
if (slash != -1) s = s.substring(0, slash);
int colon = s.indexOf(':');
if (colon != -1) s = s.substring(0, colon);
return s.trim();
}
private static void writeCertFile(File file, byte[] encoded) throws IOException {
String pem = "-----BEGIN CERTIFICATE-----\n"
+ Base64.getMimeEncoder(64, new byte[]{'\n'}).encodeToString(encoded)
+ "\n-----END CERTIFICATE-----\n";
try (FileWriter fw = new FileWriter(file)) { fw.write(pem); }
}
private static void addAlias(KeyStore ks, String alias, X509Certificate cert)
throws KeyStoreException {
if (!ks.containsAlias(alias)) {
ks.setCertificateEntry(alias, cert);
System.out.println(" keystore β '" + alias + "'");
}
}
private static void removeAlias(KeyStore ks, String alias) throws KeyStoreException {
if (ks.containsAlias(alias)) ks.deleteEntry(alias);
}
private static void persistJks(KeyStore ks) {
File jks = new File(EXPORT_DIR, "combined-truststore.jks");
try (FileOutputStream fos = new FileOutputStream(jks)) {
ks.store(fos, "changeit".toCharArray());
System.out.println("[INFO] JKS saved β " + jks.getAbsolutePath());
} catch (Exception e) {
System.out.println("[WARN] JKS save failed: " + e.getMessage());
}
}
private static String repeat(String s, int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) sb.append(s);
return sb.toString();
}
private static void printBanner() {
System.out.println(repeat("β", 60));
System.out.println(" SslFetchTest β PKIX Auto-Trust + URL Fetch");
System.out.println(" Java : " + System.getProperty("java.version")
+ " OS: " + System.getProperty("os.name"));
System.out.println(" JAVA_HOME : " + System.getProperty("java.home"));
System.out.println(repeat("β", 60));
System.out.println(" Target URLs:");
for (String u : TARGET_URLS) System.out.println(" β’ " + u);
System.out.println(repeat("β", 60));
}
// ββ Standard hostname verifier (replaces the JVM default after patching) ββ
/**
* RFC 2818-compliant hostname verifier.
* Replaces the JVM default after we call setDefaultSSLSocketFactory()
* to avoid accidentally leaving a trust-all verifier in place.
*/
static class DefaultHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
try {
// Delegate to the standard HTTPS hostname verifier
HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session);
return true;
} catch (Exception e) {
// Fall back to javax built-in
try {
javax.net.ssl.HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, session);
} catch (Exception ex) {
return false;
}
}
}
}
}
package com.royalenfield.finance.util;
import java.util.Set;
/**
- PanValidatorUtil
- Offline utility for validating Indian PAN (Permanent Account Number) structure
- before making any downstream API calls (e.g., IDFC CREATELOAN).
- PAN Format: AAAAA9999A
- [0-2] β 3 alphabetic chars : Sequential series (AAAβZZZ)
- [3] β 1 alphabetic char : Entity/Holder type (P, F, C, H, A, T, B, G, J, L)
- [4] β 1 alphabetic char : First letter of surname (Person) or entity name
- [5-8] β 4 numeric digits : Sequential number (0001β9999)
- [9] β 1 alphabetic char : Alphabetic check digit
- Reference:
- https://www.razehq.com/blog/Key-Patterns-in-PAN-Aadhaar-CIN-and-GSTIN-for-robust-Due-Diligence */ public final class PanValidatorUtil {
// -------------------------------------------------------------------------
// Regex
// -------------------------------------------------------------------------
/** Base structural regex β 5 letters, 4 digits, 1 letter. */
private static final String PAN_CARD_REGEX = "^[A-Z]{5}[0-9]{4}[A-Z]{1}$";
// -------------------------------------------------------------------------
// Enums
// -------------------------------------------------------------------------
/**
* Error codes aligned with the existing finance error catalogue.
*/
public enum PanError {
/** PAN is null or blank. */
INVALID_PAN_BLANK("1025", "PAN Card Number Is Required."),
/** PAN does not match the base 10-char alphanumeric pattern. */
INVALID_PAN_CARD_PATTERN("1029", "PAN Card Number Is Invalid."),
/**
* 4th character (index 3) is not a recognised entity/holder type.
* Valid: A, B, C, F, G, H, J, L, P, T
*/
INVALID_PAN_ENTITY_TYPE("1030", "PAN Entity/Holder Type Character (4th) Is Invalid."),
/**
* 5th character (index 4) does not match the first letter of the
* provided surname (for Persons) or entity name.
*/
INVALID_PAN_NAME_INITIAL("1031", "PAN 5th Character Does Not Match Applicant Surname Initial.");
private final String code;
private final String message;
PanError(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() { return code; }
public String getMessage() { return message; }
@Override
public String toString() {
return "[" + code + "] " + message;
}
}
/**
* Holder/Entity type encoded at PAN[3] (4th character).
*
* Per Income Tax Dept rules:
* P β Person (Individual)
* F β Firm
* C β Company / Corporation
* H β Hindu Undivided Family (HUF)
* A β Association of Persons (AOP)
* T β Trust / AOP (Trust)
* B β Body of Individuals (BOI)
* G β Government entity
* J β Artificial Juridical Person
* L β Local Authority
*/
public enum PanEntityType {
P("Person / Individual"),
F("Firm"),
C("Company / Corporation"),
H("Hindu Undivided Family (HUF)"),
A("Association of Persons (AOP)"),
T("Trust"),
B("Body of Individuals (BOI)"),
G("Government Entity"),
J("Artificial Juridical Person"),
L("Local Authority");
private final String description;
PanEntityType(String description) {
this.description = description;
}
public String getDescription() { return description; }
/** Returns true when the character maps to an Individual / Person holder. */
public boolean isPerson() {
return this == P;
}
/** Returns true when the PAN belongs to a non-individual business entity. */
public boolean isNonPerson() {
return !isPerson();
}
}
// -------------------------------------------------------------------------
// Result
// -------------------------------------------------------------------------
/**
* Immutable result object returned by every public validate* method.
*/
public static final class PanValidationResult {
private final boolean valid;
private final PanError error; // null when valid == true
private final PanEntityType entityType; // null when PAN is structurally invalid
private PanValidationResult(boolean valid, PanError error, PanEntityType entityType) {
this.valid = valid;
this.error = error;
this.entityType = entityType;
}
static PanValidationResult ok(PanEntityType entityType) {
return new PanValidationResult(true, null, entityType);
}
static PanValidationResult fail(PanError error) {
return new PanValidationResult(false, error, null);
}
static PanValidationResult fail(PanError error, PanEntityType entityType) {
return new PanValidationResult(false, error, entityType);
}
public boolean isValid() { return valid; }
public PanError getError() { return error; }
public PanEntityType getEntityType() { return entityType; }
/** Convenience: error code string, or null if valid. */
public String getErrorCode() { return error != null ? error.getCode() : null; }
public String getErrorMessage() { return error != null ? error.getMessage() : null; }
@Override
public String toString() {
return valid
? "PanValidationResult{valid=true, entityType=" + entityType + "}"
: "PanValidationResult{valid=false, error=" + error + "}";
}
}
// -------------------------------------------------------------------------
// Valid entity-type characters (for fast lookup)
// -------------------------------------------------------------------------
private static final Set<Character> VALID_ENTITY_CHARS;
static {
VALID_ENTITY_CHARS = new java.util.HashSet<>();
for (PanEntityType t : PanEntityType.values()) {
VALID_ENTITY_CHARS.add(t.name().charAt(0));
}
}
// -------------------------------------------------------------------------
// Constructor β not instantiable
// -------------------------------------------------------------------------
private PanValidatorUtil() {
throw new UnsupportedOperationException("Utility class");
}
// =========================================================================
// PUBLIC API
// =========================================================================
/**
* Validates PAN structure only (pattern + 4th char + 5th char NOT checked
* against any name). Use when no applicant name is available.
*
* Checks:
* 1. Non-blank
* 2. Matches ^[A-Z]{5}[0-9]{4}[A-Z]{1}$ (INVALID_PAN_CARD_PATTERN)
* 3. 4th character is a valid entity type (INVALID_PAN_ENTITY_TYPE)
*
* @param pan Raw PAN string from request payload (may be null).
* @return {@link PanValidationResult}
*/
public static PanValidationResult validatePattern(String pan) {
return doValidate(pan, null, null);
}
/**
* Validates PAN and cross-checks the 5th character against the applicant's
* last name initial. Intended for Individual (P-type) PANs.
*
* Checks (in order):
* 1. Non-blank
* 2. Matches base pattern (INVALID_PAN_CARD_PATTERN)
* 3. 4th character is a valid entity type (INVALID_PAN_ENTITY_TYPE)
* 4. 5th character == first letter of lastName (INVALID_PAN_NAME_INITIAL)
*
* @param pan PAN from payload.
* @param firstName Applicant first name (used for context; not validated in PAN).
* @param lastName Applicant last/surname β its initial must match PAN[4].
* @return {@link PanValidationResult}
*/
public static PanValidationResult validateWithName(String pan,
String firstName,
String lastName) {
return doValidate(pan, firstName, lastName);
}
/**
* Returns the {@link PanEntityType} decoded from a structurally valid PAN,
* or {@code null} if the PAN is invalid.
*
* @param pan PAN string (will be normalised to uppercase).
*/
public static PanEntityType getEntityType(String pan) {
if (pan == null || pan.isBlank()) return null;
String normalised = pan.trim().toUpperCase();
if (!normalised.matches(PAN_CARD_REGEX)) return null;
char entityChar = normalised.charAt(3);
try {
return PanEntityType.valueOf(String.valueOf(entityChar));
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Quick boolean check β returns {@code true} only when the PAN passes all
* structural validations (pattern + 4th char). Does NOT check name initial.
*
* @param pan PAN string.
*/
public static boolean isValidPan(String pan) {
return validatePattern(pan).isValid();
}
// =========================================================================
// PRIVATE HELPERS
// =========================================================================
private static PanValidationResult doValidate(String pan,
String firstName,
String lastName) {
// ββ Step 1: Null / blank guard
if (pan == null || pan.isBlank()) {
return PanValidationResult.fail(PanError.INVALID_PAN_BLANK);
}
final String normalised = pan.trim().toUpperCase();
// ββ Step 2: Base pattern ^[A-Z]{5}[0-9]{4}[A-Z]{1}$
if (!normalised.matches(PAN_CARD_REGEX)) {
return PanValidationResult.fail(PanError.INVALID_PAN_CARD_PATTERN);
}
// ββ Step 3: 4th character β entity / holder type
char entityChar = normalised.charAt(3);
PanEntityType entityType;
try {
entityType = PanEntityType.valueOf(String.valueOf(entityChar));
} catch (IllegalArgumentException e) {
// Character not in enum β invalid
return PanValidationResult.fail(PanError.INVALID_PAN_ENTITY_TYPE);
}
// ββ Step 4: 5th character β surname / entity-name initial (only when name supplied)
if (lastName != null && !lastName.isBlank()) {
char panNameInitial = normalised.charAt(4);
char applicantInitial = Character.toUpperCase(lastName.trim().charAt(0));
if (panNameInitial != applicantInitial) {
// Return entity type alongside the error so caller still knows the type
return PanValidationResult.fail(PanError.INVALID_PAN_NAME_INITIAL, entityType);
}
}
return PanValidationResult.ok(entityType);
}
// =========================================================================
// QUICK DEMO (remove before production)
// =========================================================================
public static void main(String[] args) {
System.out.println("=== PanValidatorUtil Demo ===\n");
record TestCase(String label, String pan, String firstName, String lastName) {}
java.util.List<TestCase> cases = java.util.List.of(
new TestCase("Valid individual (Jain β J matches PAN[4])",
"BREJJ1274F", "Rajat", "Jain"),
new TestCase("Invalid β PAN[3] 'Q' is not a valid entity char",
"BREQW1234F", "Rajat", "Jain"),
new TestCase("Invalid β PAN[3] 'M' is not a valid entity char",
"BREMW1274F", "Rajat", "Jain"),
new TestCase("Invalid β name initial mismatch (surname=Jain, PAN[4]=W)",
"BREPW1234F", "Rajat", "Jain"),
new TestCase("Valid company PAN (no name check)",
"AABCS1234A", null, null),
new TestCase("Invalid β pattern too short",
"ABCD1234F", null, null),
new TestCase("Blank PAN",
"", "Rajat", "Jain")
);
for (TestCase tc : cases) {
PanValidationResult result = tc.lastName() != null
? validateWithName(tc.pan(), tc.firstName(), tc.lastName())
: validatePattern(tc.pan());
System.out.printf("[%-55s]%n PAN: %-15s β %s%n%n",
tc.label(), tc.pan(), result);
}
}
}