package codesign import ( "crypto/x509" "fmt" "os" "github.com/blacktop/ipsw/internal/codesign/cms" "golang.org/x/crypto/pkcs12" ) type CMSConfig struct { CertChain []*x509.Certificate PrivateKey any Timestamp bool TimestampURL string Proxy string Insecure bool } func ParseP12(path, password string) (any, []*x509.Certificate, error) { certData, err := os.ReadFile(path) if err != nil { return nil, nil, fmt.Errorf("failed to read certificate file %s: %w", path, err) } blocks, err := pkcs12.ToPEM(certData, password) if err != nil { return nil, nil, fmt.Errorf("failed to parse pkcs12 file %s: %w", path, err) } var privateKey any var certs []*x509.Certificate for _, b := range blocks { switch b.Type { case "CERTIFICATE": cert, err := x509.ParseCertificate(b.Bytes) if err != nil { return nil, nil, err } certs = append(certs, cert) case "PRIVATE KEY": if privateKey, err = x509.ParsePKCS1PrivateKey(b.Bytes); err != nil { outterErr := err if privateKey, err = x509.ParseECPrivateKey(b.Bytes); err != nil { return nil, nil, fmt.Errorf("failed to parse private key: %w: %w", outterErr, err) } } default: return nil, nil, fmt.Errorf("unknown block type: %s", b.Type) } } return privateKey, certs, nil } func CreateCMSSignature(data []byte, conf *CMSConfig) ([]byte, error) { sd, err := cms.NewSignedData(data) if err != nil { return nil, err } if err = sd.Sign(conf.CertChain, conf.PrivateKey); err != nil { return nil, err } if conf.Timestamp { if err = sd.AddTimestamps(conf.TimestampURL, conf.Proxy, conf.Insecure); err != nil { return nil, fmt.Errorf("failed to add timestamps (RFC3161): %w", err) } } sd.Detached() return sd.ToDER() }