diff --git a/CHANGELOG.md b/CHANGELOG.md index dc636d57..0b1fc527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # v1.0.1 TBD +* Decrypt armored private keys if the `-apns-password` flag is specified by the user. #105 * Improved command queue handling of NotNow and other responses. #96 * Fixed data race in pubsub package. #97 * Fixed bug that would cause PushInfo Token for the device to be replaced by one for the user. #90 diff --git a/serve.go b/serve.go index 1c7cacdb..c7abb3e7 100644 --- a/serve.go +++ b/serve.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "crypto/rsa" "crypto/tls" @@ -55,8 +56,8 @@ func serve(args []string) error { flagset := flag.NewFlagSet("serve", flag.ExitOnError) var ( flServerURL = flagset.String("server-url", "", "public HTTPS url of your server") - flAPNSCertPath = flagset.String("apns-cert", "mdm.p12", "path to APNS certificate") - flAPNSKeyPass = flagset.String("apns-password", "secret", "password for your p12 APNS cert file (if using)") + flAPNSCertPath = flagset.String("apns-cert", "", "path to APNS certificate") + flAPNSKeyPass = flagset.String("apns-password", "", "password for your p12 APNS cert file (if using)") flAPNSKeyPath = flagset.String("apns-key", "", "path to key file if using .pem push cert") flTLS = flagset.Bool("tls", true, "use https") flTLSCert = flagset.String("tls-cert", "", "path to TLS certificate") @@ -395,13 +396,30 @@ func (c *config) loadPushCerts() { return } + pkeyBlock := new(bytes.Buffer) pemBlock, _ = pem.Decode(pemData) if pemBlock == nil { c.err = errors.New("invalid PEM data for privkey") return } - c.pushCert.PrivateKey, c.err = - x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + + if x509.IsEncryptedPEMBlock(pemBlock) { + b, err := x509.DecryptPEMBlock(pemBlock, []byte(c.APNSPrivateKeyPass)) + if err != nil { + c.err = fmt.Errorf("decrypting DES private key %s", err) + return + } + pkeyBlock.Write(b) + } else { + pkeyBlock.Write(pemBlock.Bytes) + } + + priv, err := x509.ParsePKCS1PrivateKey(pkeyBlock.Bytes()) + if err != nil { + c.err = fmt.Errorf("parsing pkcs1 private key: %s", err) + return + } + c.pushCert.PrivateKey = priv } type pushServiceCert struct { diff --git a/serve_test.go b/serve_test.go new file mode 100644 index 00000000..4f3f5d05 --- /dev/null +++ b/serve_test.go @@ -0,0 +1,31 @@ +package main + +import "testing" + +func TestLoadPushCerts(t *testing.T) { + keypath := "testdata/ProviderPrivateKey.key" + certpath := "testdata/pushcert.pem" + p12path := "testdata/pushcert.p12" + keysecret := "secret" + + cfg := &config{ + APNSPrivateKeyPath: keypath, + APNSCertificatePath: certpath, + APNSPrivateKeyPass: keysecret, + } + + // test separate key and cert + cfg.loadPushCerts() + if cfg.err != nil { + t.Fatal(cfg.err) + } + + // test p12 with secret + cfg.APNSCertificatePath = p12path + cfg.APNSPrivateKeyPath = "" + cfg.loadPushCerts() + if cfg.err != nil { + t.Fatal(cfg.err) + } + +} diff --git a/testdata/ProviderPrivateKey.key b/testdata/ProviderPrivateKey.key new file mode 100644 index 00000000..647c64f8 --- /dev/null +++ b/testdata/ProviderPrivateKey.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,22c0f4c89c7b1fcd + +KVnJeaMzHPrL5pHmkB6yAR9N8cvh2FKBfmJuRP61GiSR+PAI1UxsZ+627cnNZc/8 +Y2haoxAtW4k5Yl6gIQH8exf4el5yD+Ds4mEUacw7m3RV+WNL32I1+Z+jr55jetzP +nb3t9LQmsP07uigSe/oh0vumVhjxxSE31/iB2BISnEfJDsDYHFFc1XWAxNABG+JP +ttCnIHPXaFLb7jDdZnnsk2rBNsRkOPwJ27hna9b0RyHVmqMb3XZtQs2RtARTVqap +hCFSphMrS20wLqcHyJZ7v0p9MiCp71g58xs4Bl9Vh1FI86AA+AOaosA0Kl8lPa08 +ChuJf7ng3mhJ+nKRkPUK4eDtnCsNm3Xd0lQQHqRWcoUJ80Gdhla0CodJnt/BT35y +SP+j2SUJRuAvNpSeGyAcLv3wDwhMgkVTt1errZOL0tFa/ep5YwfFPMBDVNXx+lOd +AHDey8gnoGJFpwoQFdXp6TVZEJjP6kgN8njmAZmbwrFcqMdjg+eMZfAYXUAdy4aT +aMbMGwjyjUgP80iXX5OAgGchpQ+HO5otpbecAMKs6SAqAgTMbKMG2rLgZe+mlkbd +6xnwyrhkQpQHVN97gqWic2cIggyR3UL58mned/5eS3EcNTTxHJO8D7EwmIjkGVxY +fT+tdpJ6iKK6CfjIEZYFgtLafmKMxxDHaplLGnIK8VHiopwUIKxVcji9Pjp+0a1O +q9NSVZXAbt45mL2jV94hhEpt83YyBFdRYBFk5LT7AQg2ipVl51g7fO2SoTiHakzL +MrKfo+bSxDHOWefu9Z380Vmh1Mm0xDU44C8g9Jc4N7euCQtGfX5bnWuueuGwaaXL +lYrqKVuqKdc2y5d8N7KiHEHY0zsfyjFu4+zh/v2zYIBlbuM9ZGiuSlprHvy//wev +kPZDEtHNTeFXBn7qDeTGNlYK+aGQyTFOzasXcUbf3RywjpHOM/uL7rUpA11W+Ip/ +/b1h+mcADy90DMBv1xVX3vZVldwAdcqbQR0QTXxgCS1dzVdedG2N7noJVKUfbA4x +tI3NEAaBt7J7eLQm7TawRv6j5G+VB/ZS5B8WVCVbBjwwqbeccJcuzJKLBX0A7c+X +TouVuE7xpq9OK0zsaIwg7WMgKOY9ZMuMNzjKnXcSYcq5o9LHHsB1CHyOGdHkmj/J +AmME2xnyB+GCLWyN8B17hyC4MwkoNDWLnlUzL1Z3lf5jsorx+zORKho+rgg02Blw +hY02jVEcHoO8fup6GXkWtWpq2pljHD6DBg4thqwd2X3tpKBEDCcPYjMNRBmb+EZo +ZZrHMZX999BsgQip+9s6gpg4OxLbkn/wB94D+XoQYCF/rKb3ztlMHWygyjasKbWe +L5DnI4tDaO0CAYP79ixJWd8DnHy7lkDFCWA3ZdDV5brM81biJVr24b0SoV22/nT/ +wvTP0wgdRBmBmqFYZojlIWM+Gu09tVqXy8ui5oOGJThXgCyPWPrs9PSIfLyOjfDm +vUy/3yhOfCZmLEFG955P3A92ygojs/EvIrE2oWKVxf0X+T0mIVnBxeWpQ0OOVC1q +51gz/pyLNjbqxP2FUOkF5hLvRupAHhFpzbP4yZcDHUisNyUgjb4kHQ== +-----END RSA PRIVATE KEY----- diff --git a/testdata/pushcert.p12 b/testdata/pushcert.p12 new file mode 100644 index 00000000..c3cd1538 Binary files /dev/null and b/testdata/pushcert.p12 differ diff --git a/testdata/pushcert.pem b/testdata/pushcert.pem new file mode 100644 index 00000000..1328e203 --- /dev/null +++ b/testdata/pushcert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgIJAPQybPTtooGmMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV +BAMMB21kbXB1c2gwHhcNMTcwMzIxMjMyNzEwWhcNNDQwODA2MjMyNzEwWjASMRAw +DgYDVQQDDAdtZG1wdXNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +43VCT3Xw62hJ1t3HhLNsyJ/wTTaLJfyNu9zwNeNVY+yOxSaj6V5UVEpWiNIfyB+j +J7E+cUdWmEQewwXPaoNsCR4bKBElB1f45UApcrmkoqzruFI4O+yfmLJkN95fO5Sd +009VZPudFf96HdYjNpgg8ofIxAxo9dP+ReU+4QgxevIM2Hg+X+4zabDiLspfGX6Y +A5Iz/R6rFCx8rc+GEWFhkB4JE0cIV/TifP5xlOr0UFTCYQ1KUEM+30s928PFRXM4 +tsm/TcM5g0Mi2bNDiVHE0hvzxmJbiL2XjFE3VBK6Dphw9gF1GLe8D2TnZMeIAj3K +JW0sjnkmvBxRvTqTHA4LvwIDAQABo1AwTjAdBgNVHQ4EFgQUJmri9d+KEYhPyDu3 +dHsVm7eFWngwHwYDVR0jBBgwFoAUJmri9d+KEYhPyDu3dHsVm7eFWngwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAmRu4hFq4+WN7Vr9FucU+yHYcj0YV +kKfJMUSxDK/bab1DR02gxcYVR+bY/xtlcc/dA0zzzXUj97/BKLpkQCxu1+MtqMPx +bbVJnrSkGDXBXMmwn9C29WDdMjLQwS4WLrkDTEVc8W9kUqZsEBQalneKlV8u5kXi +A9tolc2Kaq2uJxKcsC2Yx1MLqpEm6bqKRlQSPM0G3uWupJBnvIQfXt0n3ErphQhf +m6BuVIs2iVvEo2W+VZ+LwfeN992Yd0DeMqvMAh6Rg/N/SGaxmMeD/Co+LHKCAzW4 +JVjDjF01dBDmWTBs+NC9euIoCBej+PjMwXUo229uA19DucZXMIed9Xyoqg== +-----END CERTIFICATE-----