ขั้นตอนหลักต่อไปนี้จะต้องมีการเชื่อมต่อที่ปลอดภัยจากผู้ออกใบรับรองซึ่งไม่ได้รับการพิจารณาว่าเชื่อถือได้โดยแพลตฟอร์ม Android
ตามที่ผู้ใช้หลายคนร้องขอฉันได้ทำการสะท้อนส่วนที่สำคัญที่สุดจากบทความบล็อกของฉันที่นี่:
- รับใบรับรองที่จำเป็นทั้งหมด (รูทและ CA ระดับกลางใด ๆ )
- สร้างที่เก็บคีย์ด้วย keytool และผู้ให้บริการBouncyCastleและอิมพอร์ต certs
- โหลดที่เก็บคีย์ในแอป android ของคุณและใช้สำหรับการเชื่อมต่อที่ปลอดภัย (ฉันแนะนำให้ใช้Apache HttpClientแทนมาตรฐาน
java.net.ssl.HttpsURLConnection
(เข้าใจง่ายกว่ามีประสิทธิภาพมากกว่า)
คว้า certs
คุณต้องได้รับใบรับรองทั้งหมดที่สร้างสายโซ่จากใบรับรองจุดสิ้นสุดจนถึงรูต CA ซึ่งหมายความว่าใบรับรอง CA ระดับกลาง (ถ้ามี) ใด ๆ และใบรับรอง Root CA คุณไม่จำเป็นต้องได้รับใบรับรองปลายทาง
สร้างที่เก็บคีย์
ดาวน์โหลดผู้ให้บริการ BouncyCastleและเก็บไว้ในตำแหน่งที่ทราบ ตรวจสอบให้แน่ใจว่าคุณสามารถเรียกใช้คำสั่ง keytool (โดยปกติจะอยู่ใต้โฟลเดอร์ bin ของการติดตั้ง JRE ของคุณ)
ตอนนี้นำเข้าใบรับรองที่ได้รับ (อย่านำเข้าใบรับรองปลายทาง) ไปยังที่เก็บคีย์ที่จัดรูปแบบ BouncyCastle
ฉันไม่ได้ทดสอบ แต่ฉันคิดว่าลำดับการนำเข้าใบรับรองเป็นสิ่งสำคัญ ซึ่งหมายความว่านำเข้าใบรับรอง CA ระดับต่ำสุดมาก่อนจากนั้นขึ้นไปจนถึงใบรับรอง Root CA
ด้วยคำสั่งต่อไปนี้ที่เก็บคีย์ใหม่ (ถ้าไม่มีอยู่) พร้อมรหัสผ่านmysecretจะถูกสร้างขึ้นและใบรับรอง Intermediate CA จะถูกนำเข้า ฉันยังกำหนดผู้ให้บริการ BouncyCastle ซึ่งสามารถพบได้ในระบบไฟล์ของฉันและรูปแบบที่เก็บคีย์ ดำเนินการคำสั่งนี้สำหรับแต่ละใบรับรองในสายโซ่
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
ตรวจสอบว่าใบรับรองถูกอิมพอร์ตอย่างถูกต้องไปยังที่เก็บคีย์:
keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
ควรส่งออกโซ่ทั้งหมด:
RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
ตอนนี้คุณสามารถคัดลอก keystore เป็นทรัพยากรดิบในแอพ android ของคุณภายใต้ res/raw/
ใช้ที่เก็บคีย์ในแอปของคุณ
ก่อนอื่นเราต้องสร้าง Apache HttpClient ที่กำหนดเองซึ่งใช้ที่เก็บคีย์ของเราสำหรับการเชื่อมต่อ HTTPS:
import org.apache.http.*
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mysecret".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
เราได้สร้าง HttpClient ที่กำหนดเองตอนนี้เราสามารถใช้มันเพื่อการเชื่อมต่อที่ปลอดภัย ตัวอย่างเช่นเมื่อเราทำการเรียก GET ไปยังทรัพยากร REST:
// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();
แค่นั้นแหละ ;)