The error message "certificate_unknown" typically occurs when Apple Push Notification service (APNs) cannot validate your SSL certificate chain. This happens most frequently when:
1. The certificate is self-signed
2. Intermediate certificates are missing
3. The certificate is expired or revoked
4. Incorrect certificate format is used
Apple's servers perform rigorous certificate validation:
- Checks the certificate chain up to a trusted root CA
- Validates the certificate is issued for APNs service
- Verifies the certificate hasn't expired
- Ensures the private key matches
First, verify your certificate using OpenSSL:
openssl pkcs12 -info -in privateKey.p12 -nodes
Check for these critical elements:
- Certificate chain completeness
- Valid "Not Before" and "Not After" dates
- Correct subject fields including:
CN=Apple Push Services: com.yourdomain.app
OU=YOUR_TEAM_ID
O=Your Company
For the JavaPNS library specifically, try these modifications:
// Modified connection code with explicit trust store
System.setProperty("javax.net.ssl.trustStore", "/path/to/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// Alternative approach with custom SSL context
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new SecureRandom());
PushNotificationManager pushManager = new PushNotificationManager();
pushManager.initializeConnection(HOST, PORT, certificate, passwd, sslContext);
When creating your .p12 file:
- Export from Keychain Access with private key
- Include all intermediate certificates
- Use strong password protection
- Verify the certificate type is "Production" or "Sandbox" as needed
Use Wireshark to monitor the SSL handshake:
Filter: ssl.handshake and ip.addr == 17.0.0.0/8
Look for:
- Client Hello
- Server Hello
- Certificate exchange
- Alert messages
Here's a more robust implementation using newer Java SSL features:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream keyStream = new FileInputStream(certificate)) {
keyStore.load(keyStream, passwd.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore, passwd.toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), null, null);
SSLSocketFactory factory = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(HOST, PORT);
socket.startHandshake();
When implementing Apple Push Notification service (APNs), the "certificate_unknown" SSL error typically occurs when the server cannot establish a trusted connection with APNs. This indicates a problem with your certificate chain or keystore configuration.
- Using an expired or revoked certificate
- Incorrect .p12 file generation
- Missing intermediate certificates
- Password mismatch
- Using production certificate with sandbox environment or vice versa
1. Validate Certificate Chain
First, check your certificate chain using OpenSSL:
openssl pkcs12 -info -in privateKey.p12 -nodes
This should show your certificate along with the Apple Worldwide Developer Relations Intermediate Certificate.
2. Verify Environment Matching
// Sandbox vs Production endpoints
private static final String SANDBOX_HOST = "gateway.sandbox.push.apple.com";
private static final String PRODUCTION_HOST = "gateway.push.apple.com";
// Make sure you're using the correct endpoint for your certificate type
Here's an improved implementation with better error handling:
import javapns.Push;
import javapns.notification.PushNotificationPayload;
import org.json.JSONException;
public class RobustPushNotification {
private static final String HOST = "gateway.sandbox.push.apple.com";
private static final int PORT = 2195;
private static final String CERT_PATH = "/path/to/certificate.p12";
private static final String PASSWORD = "your_password";
private static final String DEVICE_TOKEN = "device_token_here";
public static void main(String[] args) {
try {
// Enhanced payload with more options
PushNotificationPayload payload = PushNotificationPayload.complex();
payload.addAlert("Hello World!");
payload.addBadge(1);
payload.addSound("default");
// Push with feedback
Push.payload(payload, CERT_PATH, PASSWORD, false, DEVICE_TOKEN);
} catch (JSONException e) {
System.err.println("JSON Error: " + e.getMessage());
} catch (Exception e) {
System.err.println("Push Failed: " + e.getMessage());
e.printStackTrace();
}
}
}
- Download both certificates from Apple Developer portal
- Combine them into a single .p12 file:
openssl pkcs12 -export \ -in aps_development.cer \ -inkey yourPrivateKey.key \ -certfile AppleWWDRCA.cer \ -out pushcert.p12
- Verify the certificate is not expired
- Ensure the certificate matches your app bundle ID
For more control, use the PushNotificationManager directly:
PushNotificationManager pushManager = new PushNotificationManager();
pushManager.initializeConnection(
HOST,
PORT,
CERT_PATH,
PASSWORD,
SSLConnectionHelper.KEYSTORE_TYPE_PKCS12
);
// Send multiple notifications efficiently
List<PushedNotification> notifications = pushManager.sendNotifications(
payload,
Collections.singletonList(DEVICE_TOKEN)
);
pushManager.stopConnection();