慕森卡
在https://www.jamf.com/jamf-nation/discussions/13812/problems-importing-cert-via-terminal之后,我通过编写包含证书的配置文件并使用profiles命令行工具安装它来解决这个问题而不是security直接使用那个。这是改编后的脚本:package mainimport ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "io/ioutil" "math/big" "os" "os/exec" "strings" "time" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" "howett.net/plist")// Payload represents a configuration profile's payload. (Adapted from https://github.com/micromdm/micromdm/blob/master/mdm/enroll/profile.go).type Payload struct { PayloadType string `json:"type"` PayloadVersion int `json:"version"` PayloadIdentifier string `json:"identifier"` PayloadUUID string `json:"uuid"` PayloadDisplayName string `json:"displayname" plist:",omitempty"` PayloadDescription string `json:"description,omitempty" plist:",omitempty"` PayloadOrganization string `json:"organization,omitempty" plist:",omitempty"` PayloadScope string `json:"scope" plist:",omitempty"` PayloadContent interface{} `json:"content,omitempty" plist:"PayloadContent,omitempty"`}// Profile represents a configuration profile (cf. https://developer.apple.com/business/documentation/Configuration-Profile-Reference.pdf)type Profile struct { PayloadContent []interface{} `json:"content,omitempty"` PayloadDescription string `json:"description,omitempty" plist:",omitempty"` PayloadDisplayName string `json:"displayname,omitempty" plist:",omitempty"` PayloadExpirationDate *time.Time `json:"expiration_date,omitempty" plist:",omitempty"` PayloadIdentifier string `json:"identifier"` PayloadOrganization string `json:"organization,omitempty" plist:",omitempty"` PayloadUUID string `json:"uuid"` PayloadRemovalDisallowed bool `json:"removal_disallowed" plist:",omitempty"` PayloadType string `json:"type"` PayloadVersion int `json:"version"` PayloadScope string `json:"scope" plist:",omitempty"` RemovalDate *time.Time `json:"removal_date" plist:"-" plist:",omitempty"` DurationUntilRemoval float32 `json:"duration_until_removal" plist:",omitempty"` ConsentText map[string]string `json:"consent_text" plist:",omitempty"`}type CertificatePayload struct { Payload PayloadContent []byte PayloadCertificateFileName string `plist:",omitempty"` Password string `plist:",omitempty"` AllowAllAppsAccess bool `plist:",omitempty"`}// NewProfile creates a new configuration profilefunc NewProfile() *Profile { payloadUUID := uuid.NewV4() return &Profile{ PayloadVersion: 1, PayloadType: "Configuration", PayloadUUID: payloadUUID.String(), }}// NewPayload creates a new payloadfunc NewPayload(payloadType string) *Payload { payloadUUID := uuid.NewV4() return &Payload{ PayloadVersion: 1, PayloadType: payloadType, PayloadUUID: payloadUUID.String(), }}func NewCertificateProfile(certPEM []byte) *Profile { profile := NewProfile() profile.PayloadDescription = "Awesome Payload" profile.PayloadDisplayName = "Awesome Certificate" profile.PayloadIdentifier = "com.awesomeness.certificate" profile.PayloadScope = "System" profile.PayloadOrganization = "Awesomeness, Inc." payload := NewPayload("com.apple.security.pem") payload.PayloadDescription = "Awesome Certificate" payload.PayloadDisplayName = "Awesome Certificate" payload.PayloadOrganization = "Awesomeness, Inc." payload.PayloadIdentifier = profile.PayloadIdentifier + "." + payload.PayloadUUID certificatePayload := CertificatePayload{ Payload: *payload, PayloadContent: certPEM, } profile.PayloadContent = []interface{}{certificatePayload} return profile}func generateSelfSignedCertificate(keyFileName, certFileName string) { // Generate a self-signed certificate (adapted from https://golang.org/src/crypto/tls/generate_cert.go) key, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { logrus.WithError(err).Fatal("generate key") } keyFile, err := os.Create(keyFileName) if err != nil { logrus.WithError(err).Fatal("create key file") } if err = pem.Encode(keyFile, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }); err != nil { logrus.WithError(err).Fatal("marshal private key") } keyFile.Close() template := x509.Certificate{ SerialNumber: big.NewInt(42), Subject: pkix.Name{ Country: []string{"US"}, Organization: []string{"Awesomeness, Inc."}, OrganizationalUnit: []string{"Awesomeness Dept."}, CommonName: "Awesomeness 4, Inc.", }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, IsCA: true, BasicConstraintsValid: true, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) if err != nil { logrus.WithError(err).Fatal("failed to create certificate") } certFile, err := os.Create(certFileName) if err != nil { logrus.WithError(err).Fatal("create cert file") } if err = pem.Encode(certFile, &pem.Block{ Type: "CERTIFICATE", Bytes: derBytes, }); err != nil { logrus.WithError(err).Fatal("encode certificate") } certFile.Close()}func main() { keyFileName := "key.pem" certFileName := "cert.pem" profileFileName := "certificate.mobileconfig" generateSelfSignedCertificate(keyFileName, certFileName) certPEM, err := ioutil.ReadFile(certFileName) if err != nil { logrus.WithError(err).Fatal("read certificate file") } certificateProfile := NewCertificateProfile(certPEM) mobileconfig, err := plist.MarshalIndent(certificateProfile, plist.XMLFormat, "\t") if err != nil { logrus.WithError(err).Fatal("marshal plist") } if err := ioutil.WriteFile(profileFileName, mobileconfig, 0755); err != nil { logrus.WithError(err).Fatal("write mobileconfig to file") } args := []string{"install", "-path", profileFileName} output, err := exec.Command("/usr/bin/profiles", args...).CombinedOutput() if err != nil { logrus.Fatalf("%s: %v - %s", "/usr/bin/profiles"+strings.Join(args, " "), err, output) }}使用 运行此命令后sudo -E go run add_certificate.go,具有通用名称Awesomeness 4, Inc.的证书在我的钥匙串中显示为受信任的证书: